diff --git a/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx b/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx
index 1c1b133206..a0df36ad10 100644
--- a/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx
+++ b/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx
@@ -1,5 +1,4 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
+import { render, screen, initializeMocks } from '@src/testUtils';
import * as CourseAuthoringContext from '@src/CourseAuthoringContext';
import * as CourseDetailsApi from '@src/data/apiHooks';
@@ -17,6 +16,7 @@ jest.mock('@src/content-tags-drawer', () => ({
describe('OutlineAlignSidebar', () => {
beforeEach(() => {
+ initializeMocks();
jest
.spyOn(CourseAuthoringContext, 'useCourseAuthoringContext')
.mockReturnValue({
diff --git a/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx b/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx
index 73593d0572..d8ee533a86 100644
--- a/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx
+++ b/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx
@@ -1,11 +1,12 @@
-import { SchoolOutline } from '@openedx/paragon/icons';
-import { ContentTagsDrawer } from '@src/content-tags-drawer';
import { useContentData } from '@src/content-tags-drawer/data/apiHooks';
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
import { useCourseDetails } from '@src/data/apiHooks';
-import { SidebarTitle } from '@src/generic/sidebar';
+import { AlignSidebar } from '@src/generic/sidebar/AlignSidebar';
import { useOutlineSidebarContext } from './OutlineSidebarContext';
+/**
+ * Align sidebar for course or selected containers.
+ */
export const OutlineAlignSidebar = () => {
const { courseId } = useCourseAuthoringContext();
const { currentContainerId } = useOutlineSidebarContext();
@@ -21,19 +22,13 @@ export const OutlineAlignSidebar = () => {
} = useContentData(currentContainerId);
return (
-
-
-
-
+
);
};
diff --git a/src/course-unit/CourseUnit.test.jsx b/src/course-unit/CourseUnit.test.jsx
index 2d58f1d8b6..ca564288bf 100644
--- a/src/course-unit/CourseUnit.test.jsx
+++ b/src/course-unit/CourseUnit.test.jsx
@@ -2917,4 +2917,20 @@ describe('', () => {
render();
expect(await screen.findByText('Access: 3 Groups')).toBeInTheDocument();
});
+
+ it('opens the align sidebar on postMessage event', async () => {
+ setConfig({
+ ...getConfig(),
+ ENABLE_TAGGING_TAXONOMY_PAGES: 'true',
+ ENABLE_UNIT_PAGE_NEW_DESIGN: 'true',
+ });
+
+ render();
+
+ await screen.findByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+
+ simulatePostMessageEvent(messageTypes.openManageTags, { contentId: blockId });
+
+ await screen.findByText('Align');
+ });
});
diff --git a/src/course-unit/unit-sidebar/UnitAlignSidebar.test.tsx b/src/course-unit/unit-sidebar/UnitAlignSidebar.test.tsx
new file mode 100644
index 0000000000..83f63b6af0
--- /dev/null
+++ b/src/course-unit/unit-sidebar/UnitAlignSidebar.test.tsx
@@ -0,0 +1,33 @@
+import { render, screen, initializeMocks } from '@src/testUtils';
+import { UnitAlignSidebar } from './UnitAlignSidebar';
+import { UnitSidebarProvider } from './UnitSidebarContext';
+
+jest.mock('@src/content-tags-drawer', () => ({
+ ContentTagsDrawer: jest.fn(({ id, variant }) => (
+
+ drawer-mock-{id}-{variant}
+
+ )),
+}));
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ blockId: 'unit-id-1' }),
+}));
+
+describe('OutlineAlignSidebar', () => {
+ beforeEach(() => {
+ initializeMocks();
+ });
+
+ it('renders ContentTagsDrawer with the correct id and variant', () => {
+ render();
+
+ const drawer = screen.getByTestId('content-tags-drawer');
+
+ expect(drawer).toBeInTheDocument();
+ expect(drawer).toHaveTextContent(
+ 'drawer-mock-unit-id-1-component',
+ );
+ });
+});
diff --git a/src/course-unit/unit-sidebar/UnitAlignSidebar.tsx b/src/course-unit/unit-sidebar/UnitAlignSidebar.tsx
new file mode 100644
index 0000000000..6d4448e7fd
--- /dev/null
+++ b/src/course-unit/unit-sidebar/UnitAlignSidebar.tsx
@@ -0,0 +1,28 @@
+import { useParams } from 'react-router-dom';
+import { useContentData } from '@src/content-tags-drawer/data/apiHooks';
+import { AlignSidebar } from '@src/generic/sidebar/AlignSidebar';
+import { useUnitSidebarContext } from './UnitSidebarContext';
+
+/**
+ * Align sidebar for unit or selected components.
+ */
+export const UnitAlignSidebar = () => {
+ const { blockId } = useParams();
+ const { currentComponentId } = useUnitSidebarContext();
+
+ const sidebarContentId = currentComponentId || blockId;
+
+ const {
+ data: contentData,
+ } = useContentData(sidebarContentId);
+
+ return (
+
+ );
+};
diff --git a/src/course-unit/unit-sidebar/UnitSidebar.tsx b/src/course-unit/unit-sidebar/UnitSidebar.tsx
index 19a3668718..a866b0bec9 100644
--- a/src/course-unit/unit-sidebar/UnitSidebar.tsx
+++ b/src/course-unit/unit-sidebar/UnitSidebar.tsx
@@ -2,7 +2,7 @@ import { Sidebar } from '@src/generic/sidebar';
import LegacySidebar, { LegacySidebarProps } from '../legacy-sidebar';
import { useUnitSidebarContext } from './UnitSidebarContext';
import { isUnitPageNewDesignEnabled } from '../utils';
-import { UNIT_SIDEBAR_PAGES } from './constants';
+import { getUnitSidebarPages } from './sidebarPages';
export type UnitSidebarProps = {
legacySidebarProps: LegacySidebarProps,
@@ -26,7 +26,7 @@ export const UnitSidebar = ({
return (
;
interface UnitSidebarContextData {
currentPageKey: UnitSidebarPageKeys;
- setCurrentPageKey: (pageKey: UnitSidebarPageKeys) => void;
+ setCurrentPageKey: (pageKey: UnitSidebarPageKeys, componentId?: string) => void;
currentTabKey?: string;
setCurrentTabKey: (tabKey: string) => void;
+ // The Id of the component used in the current sidebar page
+ // The component is not necessarily selected to open a selected sidebar.
+ // Example: Align sidebar
+ currentComponentId?: string;
isOpen: boolean;
open: () => void;
toggle: () => void;
@@ -20,12 +25,22 @@ interface UnitSidebarContextData {
const UnitSidebarContext = createContext(undefined);
export const UnitSidebarProvider = ({ children }: { children?: React.ReactNode }) => {
- const [currentPageKey, setCurrentPageKeyState] = useState('info');
+ const [currentPageKey, setCurrentPageKeyState] = useStateWithUrlSearchParam(
+ 'info',
+ 'sidebar',
+ (value: string) => value as UnitSidebarPageKeys,
+ (value: UnitSidebarPageKeys) => value,
+ );
const [currentTabKey, setCurrentTabKey] = useState();
+ const [currentComponentId, setCurrentComponentId] = useState();
const [isOpen, open,, toggle] = useToggle(true);
- const setCurrentPageKey = useCallback(/* istanbul ignore next */ (pageKey: UnitSidebarPageKeys) => {
+ const setCurrentPageKey = useCallback(/* istanbul ignore next */ (
+ pageKey: UnitSidebarPageKeys,
+ componentId?: string,
+ ) => {
setCurrentPageKeyState(pageKey);
+ setCurrentComponentId(componentId);
open();
}, [open]);
@@ -35,6 +50,7 @@ export const UnitSidebarProvider = ({ children }: { children?: React.ReactNode }
setCurrentPageKey,
currentTabKey,
setCurrentTabKey,
+ currentComponentId,
isOpen,
open,
toggle,
@@ -44,6 +60,7 @@ export const UnitSidebarProvider = ({ children }: { children?: React.ReactNode }
setCurrentPageKey,
currentTabKey,
setCurrentTabKey,
+ currentComponentId,
isOpen,
open,
toggle,
diff --git a/src/course-unit/unit-sidebar/constants.ts b/src/course-unit/unit-sidebar/constants.ts
deleted file mode 100644
index 8bfbe7e003..0000000000
--- a/src/course-unit/unit-sidebar/constants.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Info } from '@openedx/paragon/icons';
-import { SidebarPage } from '@src/generic/sidebar';
-import messages from './messages';
-import { UnitInfoSidebar } from './unit-info/UnitInfoSidebar';
-
-export type UnitSidebarPageKeys = 'info';
-
-/**
- * Sidebar pages for the unit sidebar
- *
- * This has been separated from the context to avoid a cyclical import
- * if you want to use the context in the sidebar pages.
- */
-export const UNIT_SIDEBAR_PAGES: Record = {
- info: {
- component: UnitInfoSidebar,
- icon: Info,
- title: messages.sidebarButtonInfo,
- },
-};
diff --git a/src/course-unit/unit-sidebar/messages.ts b/src/course-unit/unit-sidebar/messages.ts
index 96c3326ce1..0abbcfe275 100644
--- a/src/course-unit/unit-sidebar/messages.ts
+++ b/src/course-unit/unit-sidebar/messages.ts
@@ -6,6 +6,11 @@ const messages = defineMessages({
defaultMessage: 'Info',
description: 'Label of the button for the Info sidebar',
},
+ sidebarButtonAlign: {
+ id: 'course-authoring.unit-page.sidebar.info.sidebar-button-align',
+ defaultMessage: 'Align',
+ description: 'Label of the button for the Align sidebar',
+ },
});
export default messages;
diff --git a/src/course-unit/unit-sidebar/sidebarPages.ts b/src/course-unit/unit-sidebar/sidebarPages.ts
new file mode 100644
index 0000000000..ee5a431f2c
--- /dev/null
+++ b/src/course-unit/unit-sidebar/sidebarPages.ts
@@ -0,0 +1,35 @@
+import { getConfig } from '@edx/frontend-platform';
+import { Info, Tag } from '@openedx/paragon/icons';
+import { SidebarPage } from '@src/generic/sidebar';
+import messages from './messages';
+import { UnitInfoSidebar } from './unit-info/UnitInfoSidebar';
+import { UnitAlignSidebar } from './UnitAlignSidebar';
+
+export type UnitSidebarPages = {
+ info: SidebarPage;
+ align?: SidebarPage;
+};
+
+/**
+ * Sidebar pages for the unit sidebar
+ *
+ * This has been separated from the context to avoid a cyclical import
+ * if you want to use the context in the sidebar pages.
+ */
+export const getUnitSidebarPages = (): UnitSidebarPages => {
+ const showAlignSidebar = getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true';
+ return {
+ info: {
+ component: UnitInfoSidebar,
+ icon: Info,
+ title: messages.sidebarButtonInfo,
+ },
+ ...(showAlignSidebar && {
+ align: {
+ component: UnitAlignSidebar,
+ icon: Tag,
+ title: messages.sidebarButtonAlign,
+ },
+ }),
+ };
+};
diff --git a/src/course-unit/xblock-container-iframe/index.tsx b/src/course-unit/xblock-container-iframe/index.tsx
index 5b67b340af..309ea686b1 100644
--- a/src/course-unit/xblock-container-iframe/index.tsx
+++ b/src/course-unit/xblock-container-iframe/index.tsx
@@ -39,12 +39,15 @@ import {
AccessManagedXBlockDataTypes,
} from './types';
import { formatAccessManagedXBlockData, getIframeUrl, getLegacyEditModalUrl } from './utils';
+import { useUnitSidebarContext } from '../unit-sidebar/UnitSidebarContext';
+import { isUnitPageNewDesignEnabled } from '../utils';
const XBlockContainerIframe: FC = ({
courseId, blockId, unitXBlockActions, courseVerticalChildren, handleConfigureSubmit, isUnitVerticalType,
}) => {
const intl = useIntl();
const dispatch = useDispatch();
+ const { setCurrentPageKey } = useUnitSidebarContext();
// Useful to reload iframe
const [iframeKey, setIframeKey] = useState(0);
@@ -169,8 +172,13 @@ const XBlockContainerIframe: FC = ({
};
const handleOpenManageTagsModal = (id: string) => {
- setConfigureXBlockId(id);
- openManageTagsModal();
+ if (isUnitPageNewDesignEnabled()) {
+ setCurrentPageKey('align', id);
+ } else {
+ // Legacy manage tags modal
+ setConfigureXBlockId(id);
+ openManageTagsModal();
+ }
};
const handleShowProcessingNotification = (variant: string) => {
diff --git a/src/generic/sidebar/AlignSidebar.tsx b/src/generic/sidebar/AlignSidebar.tsx
new file mode 100644
index 0000000000..030c9319b7
--- /dev/null
+++ b/src/generic/sidebar/AlignSidebar.tsx
@@ -0,0 +1,25 @@
+import { ContentTagsDrawer } from '@src/content-tags-drawer';
+import { SchoolOutline } from '@openedx/paragon/icons';
+import { SidebarTitle } from './SidebarTitle';
+
+export interface AlignSidebarProps {
+ contentId: string;
+ title: string;
+}
+
+/**
+ * Sidebar that renders Align Sidebar (manage tags sidebar)
+ * for the given content.
+ */
+export const AlignSidebar = ({ contentId, title }: AlignSidebarProps) => (
+
+
+
+
+);