diff --git a/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx b/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx
index 904b0cf6e..13376d83e 100644
--- a/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx
+++ b/src/course-outline/outline-sidebar/OutlineAlignSidebar.test.tsx
@@ -1,4 +1,4 @@
-import { initializeMocks, render, screen } from '@src/testUtils';
+import { render, screen, initializeMocks } from '@src/testUtils';
import * as CourseAuthoringContext from '@src/CourseAuthoringContext';
import * as CourseDetailsApi from '@src/data/apiHooks';
diff --git a/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx b/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx
index acf10b88f..486b9963f 100644
--- a/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx
+++ b/src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx
@@ -1,10 +1,11 @@
-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 { 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,
@@ -24,20 +25,14 @@ export const OutlineAlignSidebar = () => {
};
return (
-
-
-
-
+
);
};
diff --git a/src/course-unit/CourseUnit.test.jsx b/src/course-unit/CourseUnit.test.jsx
index 97a602a65..dda89e58a 100644
--- a/src/course-unit/CourseUnit.test.jsx
+++ b/src/course-unit/CourseUnit.test.jsx
@@ -2928,6 +2928,22 @@ describe('', () => {
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');
+ });
+
describe('Add sidebar', () => {
let user;
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 000000000..95333ab2d
--- /dev/null
+++ b/src/course-unit/unit-sidebar/UnitAlignSidebar.test.tsx
@@ -0,0 +1,39 @@
+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' }),
+}));
+
+const renderComponent = () => render(
+
+
+ ,
+);
+
+describe('OutlineAlignSidebar', () => {
+ beforeEach(() => {
+ initializeMocks();
+ });
+
+ it('renders ContentTagsDrawer with the correct id and variant', () => {
+ renderComponent();
+
+ 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 000000000..8e095a87e
--- /dev/null
+++ b/src/course-unit/unit-sidebar/UnitAlignSidebar.tsx
@@ -0,0 +1,36 @@
+import { useParams } from 'react-router-dom';
+import { useContentData } from '@src/content-tags-drawer/data/apiHooks';
+import { AlignSidebar } from '@src/generic/sidebar/AlignSidebar';
+import { useCallback } from 'react';
+import { useUnitSidebarContext } from './UnitSidebarContext';
+
+/**
+ * Align sidebar for unit or selected components.
+ */
+export const UnitAlignSidebar = () => {
+ const { blockId } = useParams();
+ const { currentComponentId, setCurrentPageKey } = useUnitSidebarContext();
+
+ const sidebarContentId = currentComponentId || blockId;
+
+ const {
+ data: contentData,
+ } = useContentData(sidebarContentId);
+
+ const handleBack = useCallback(() => {
+ // Set the align sidebar without current component to back
+ // to unit align sidebar.
+ setCurrentPageKey('align');
+ }, [setCurrentPageKey]);
+
+ return (
+
+ );
+};
diff --git a/src/course-unit/unit-sidebar/UnitSidebarContext.tsx b/src/course-unit/unit-sidebar/UnitSidebarContext.tsx
index 13c197544..d76ff17f8 100644
--- a/src/course-unit/unit-sidebar/UnitSidebarContext.tsx
+++ b/src/course-unit/unit-sidebar/UnitSidebarContext.tsx
@@ -3,15 +3,20 @@ import {
} from 'react';
import { SidebarPage } from '@src/generic/sidebar';
import { useToggle } from '@openedx/paragon';
+import { useStateWithUrlSearchParam } from '@src/hooks';
-export type UnitSidebarPageKeys = 'info' | 'add';
+export type UnitSidebarPageKeys = 'info' | 'add' | 'align';
export type UnitSidebarPages = Record;
interface UnitSidebarContextData {
currentPageKey: UnitSidebarPageKeys;
- setCurrentPageKey: (pageKey: UnitSidebarPageKeys) => void;
+ setCurrentPageKey: (pageKey: UnitSidebarPageKeys, componentId?: string) => void;
currentTabKey?: string;
setCurrentTabKey: (tabKey: string | undefined) => 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;
@@ -27,14 +32,23 @@ export const UnitSidebarProvider = ({
children?: React.ReactNode,
readOnly: boolean,
}) => {
- 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) => {
- // Reset tab
+ const setCurrentPageKey = useCallback(/* istanbul ignore next */ (
+ pageKey: UnitSidebarPageKeys,
+ componentId?: string,
+ ) => {
setCurrentTabKey(undefined);
setCurrentPageKeyState(pageKey);
+ setCurrentComponentId(componentId);
open();
}, [open]);
@@ -44,6 +58,7 @@ export const UnitSidebarProvider = ({
setCurrentPageKey,
currentTabKey,
setCurrentTabKey,
+ currentComponentId,
isOpen,
open,
toggle,
@@ -54,6 +69,7 @@ export const UnitSidebarProvider = ({
setCurrentPageKey,
currentTabKey,
setCurrentTabKey,
+ currentComponentId,
isOpen,
open,
toggle,
diff --git a/src/course-unit/unit-sidebar/messages.ts b/src/course-unit/unit-sidebar/messages.ts
index b5e372b79..fe4f04d22 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',
+ },
sidebarButtonAdd: {
id: 'course-authoring.unit-page.sidebar.add.sidebar-button-add',
defaultMessage: 'Add',
diff --git a/src/course-unit/unit-sidebar/sidebarPages.ts b/src/course-unit/unit-sidebar/sidebarPages.ts
index 1d09d85c7..47b6ef95a 100644
--- a/src/course-unit/unit-sidebar/sidebarPages.ts
+++ b/src/course-unit/unit-sidebar/sidebarPages.ts
@@ -1,7 +1,9 @@
-import { Info, Plus } from '@openedx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform';
+import { Info, Tag, Plus } from '@openedx/paragon/icons';
import { SidebarPage } from '@src/generic/sidebar';
import messages from './messages';
import { UnitInfoSidebar } from './unit-info/UnitInfoSidebar';
+import { UnitAlignSidebar } from './UnitAlignSidebar';
import { AddSidebar } from './AddSidebar';
import { useUnitSidebarContext } from './UnitSidebarContext';
@@ -18,6 +20,7 @@ export type UnitSidebarPages = {
* if you want to use the context in the sidebar pages.
*/
export const useUnitSidebarPages = (): UnitSidebarPages => {
+ const showAlignSidebar = getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true';
const { readOnly } = useUnitSidebarContext();
return {
info: {
@@ -32,5 +35,12 @@ export const useUnitSidebarPages = (): UnitSidebarPages => {
title: messages.sidebarButtonAdd,
},
}),
+ ...(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 2d449b0d0..bacf48cfe 100644
--- a/src/course-unit/xblock-container-iframe/index.tsx
+++ b/src/course-unit/xblock-container-iframe/index.tsx
@@ -39,6 +39,8 @@ import {
AccessManagedXBlockDataTypes,
} from './types';
import { formatAccessManagedXBlockData, getIframeUrl, getLegacyEditModalUrl } from './utils';
+import { useUnitSidebarContext } from '../unit-sidebar/UnitSidebarContext';
+import { isUnitPageNewDesignEnabled } from '../utils';
const XBlockContainerIframe: FC = ({
courseId,
@@ -51,6 +53,7 @@ const XBlockContainerIframe: FC = ({
}) => {
const intl = useIntl();
const dispatch = useDispatch();
+ const { setCurrentPageKey } = useUnitSidebarContext();
// Useful to reload iframe
const [iframeKey, setIframeKey] = useState(0);
@@ -175,8 +178,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/block-type-utils/index.scss b/src/generic/block-type-utils/index.scss
index b6fb80608..b69b041ad 100644
--- a/src/generic/block-type-utils/index.scss
+++ b/src/generic/block-type-utils/index.scss
@@ -285,7 +285,7 @@
}
}
-.icon-with-border-chapter {
+.icon-with-border-section {
border: 1px solid var(--content-library-container-section-color);
.pgn__icon {
@@ -293,7 +293,7 @@
}
}
-.icon-with-border-sequential {
+.icon-with-border-subsection {
border: 1px solid var(--content-library-container-subsection-color);
.pgn__icon {
@@ -301,7 +301,7 @@
}
}
-.icon-with-border-vertical {
+.icon-with-border-unit {
border: 1px solid var(--content-library-container-unit-color);
.pgn__icon {
diff --git a/src/generic/sidebar/AlignSidebar.tsx b/src/generic/sidebar/AlignSidebar.tsx
new file mode 100644
index 000000000..0a38f07c8
--- /dev/null
+++ b/src/generic/sidebar/AlignSidebar.tsx
@@ -0,0 +1,27 @@
+import { ContentTagsDrawer } from '@src/content-tags-drawer';
+import { SchoolOutline } from '@openedx/paragon/icons';
+import { SidebarTitle } from './SidebarTitle';
+
+export interface AlignSidebarProps {
+ contentId: string;
+ title: string;
+ onBackBtnClick?: () => void;
+}
+
+/**
+ * Sidebar that renders Align Sidebar (manage tags sidebar)
+ * for the given content.
+ */
+export const AlignSidebar = ({ contentId, title, onBackBtnClick }: AlignSidebarProps) => (
+
+
+
+
+);