diff --git a/src/content-tags-drawer/tags-sidebar-controls/TagsSidebarBody.jsx b/src/content-tags-drawer/tags-sidebar-controls/TagsSidebarBody.tsx similarity index 79% rename from src/content-tags-drawer/tags-sidebar-controls/TagsSidebarBody.jsx rename to src/content-tags-drawer/tags-sidebar-controls/TagsSidebarBody.tsx index ca7659a17f..4089efb2d7 100644 --- a/src/content-tags-drawer/tags-sidebar-controls/TagsSidebarBody.jsx +++ b/src/content-tags-drawer/tags-sidebar-controls/TagsSidebarBody.tsx @@ -1,5 +1,4 @@ -// @ts-check -import React, { useState, useMemo } from 'react'; +import { useState, useMemo } from 'react'; import { Card, Stack, Button, Collapsible, Icon, } from '@openedx/paragon'; @@ -10,10 +9,19 @@ import { ContentTagsDrawerSheet } from '..'; import messages from '../messages'; import { useContentTaxonomyTagsData } from '../data/apiHooks'; +import type { ContentTaxonomyTagData, Tag } from '../data/types'; import { LoadingSpinner } from '../../generic/Loading'; import TagsTree from '../TagsTree'; -const TagsSidebarBody = () => { +interface TagsSidebarBodyProps { + readOnly: boolean +} + +type TagTree = { + [key: string]: { children: TagTree, canChangeObjecttag: boolean, canDeleteObjecttag: boolean } +}; + +const TagsSidebarBody = ({ readOnly }: TagsSidebarBodyProps) => { const intl = useIntl(); const [showManageTags, setShowManageTags] = useState(false); const contentId = useParams().blockId; @@ -24,8 +32,8 @@ const TagsSidebarBody = () => { isSuccess: isContentTaxonomyTagsLoaded, } = useContentTaxonomyTagsData(contentId || ''); - const buildTagsTree = (contentTags) => { - const resultTree = {}; + const buildTagsTree = (contentTags: Tag[]) => { + const resultTree: TagTree = {}; contentTags.forEach(item => { let currentLevel = resultTree; @@ -46,7 +54,7 @@ const TagsSidebarBody = () => { }; const tree = useMemo(() => { - const result = []; + const result: (Omit & { tags: TagTree })[] = []; if (isContentTaxonomyTagsLoaded && contentTaxonomyTagsData) { contentTaxonomyTagsData.taxonomies.forEach((taxonomy) => { result.push({ @@ -88,7 +96,13 @@ const TagsSidebarBody = () => { )} - @@ -102,6 +116,4 @@ const TagsSidebarBody = () => { ); }; -TagsSidebarBody.propTypes = {}; - export default TagsSidebarBody; diff --git a/src/content-tags-drawer/tags-sidebar-controls/index.jsx b/src/content-tags-drawer/tags-sidebar-controls/index.tsx similarity index 54% rename from src/content-tags-drawer/tags-sidebar-controls/index.jsx rename to src/content-tags-drawer/tags-sidebar-controls/index.tsx index 98ffc5e7c4..c2ecb48553 100644 --- a/src/content-tags-drawer/tags-sidebar-controls/index.jsx +++ b/src/content-tags-drawer/tags-sidebar-controls/index.tsx @@ -1,10 +1,14 @@ import TagsSidebarHeader from './TagsSidebarHeader'; import TagsSidebarBody from './TagsSidebarBody'; -const TagsSidebarControls = () => ( +interface TagsSidebarControlsProps { + readOnly: boolean, +} + +const TagsSidebarControls = ({ readOnly }: TagsSidebarControlsProps) => ( <> - + ); diff --git a/src/course-outline/card-header/CardHeader.jsx b/src/course-outline/card-header/CardHeader.jsx index 0012b78b80..eda1ff6be4 100644 --- a/src/course-outline/card-header/CardHeader.jsx +++ b/src/course-outline/card-header/CardHeader.jsx @@ -136,6 +136,8 @@ const CardHeader = ({ alt={intl.formatMessage(messages.altButtonEdit)} iconAs={EditIcon} onClick={onClickEdit} + // @ts-ignore + disabled={isDisabledEditField} /> )} @@ -178,6 +180,7 @@ const CardHeader = ({ {intl.formatMessage(messages.menuConfigure)} @@ -185,6 +188,7 @@ const CardHeader = ({ {getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true' && ( {intl.formatMessage(messages.menuManageTags)} diff --git a/src/course-outline/card-header/CardHeader.test.jsx b/src/course-outline/card-header/CardHeader.test.jsx index d589627173..e694db4882 100644 --- a/src/course-outline/card-header/CardHeader.test.jsx +++ b/src/course-outline/card-header/CardHeader.test.jsx @@ -240,6 +240,35 @@ describe('', () => { expect(await findByTestId('subsection-edit-field')).toBeDisabled(); }); + it('check editing is enabled when isDisabledEditField is false', async () => { + const { getByTestId } = renderComponent({ + ...cardHeaderProps, + }); + + expect(getByTestId('subsection-edit-button')).toBeEnabled(); + + // Ensure menu items related to editing are enabled + const menuButton = getByTestId('subsection-card-header__menu-button'); + await act(async () => fireEvent.click(menuButton)); + expect(await getByTestId('subsection-card-header__menu-configure-button')).not.toHaveAttribute('aria-disabled'); + expect(await getByTestId('subsection-card-header__menu-manage-tags-button')).not.toHaveAttribute('aria-disabled'); + }); + + it('check editing is disabled when isDisabledEditField is true', async () => { + const { getByTestId } = renderComponent({ + ...cardHeaderProps, + isDisabledEditField: true, + }); + + expect(await getByTestId('subsection-edit-button')).toBeDisabled(); + + // Ensure menu items related to editing are disabled + const menuButton = getByTestId('subsection-card-header__menu-button'); + await act(async () => fireEvent.click(menuButton)); + expect(await getByTestId('subsection-card-header__menu-configure-button')).toHaveAttribute('aria-disabled', 'true'); + expect(await getByTestId('subsection-card-header__menu-manage-tags-button')).toHaveAttribute('aria-disabled', 'true'); + }); + it('calls onClickDelete when item is clicked', async () => { const { findByText, findByTestId } = renderComponent(); diff --git a/src/course-outline/unit-card/UnitCard.jsx b/src/course-outline/unit-card/UnitCard.jsx index 00ac4cf50d..bd0b58e559 100644 --- a/src/course-outline/unit-card/UnitCard.jsx +++ b/src/course-outline/unit-card/UnitCard.jsx @@ -9,6 +9,7 @@ import { useSearchParams } from 'react-router-dom'; import CourseOutlineUnitCardExtraActionsSlot from '../../plugin-slots/CourseOutlineUnitCardExtraActionsSlot'; import { setCurrentItem, setCurrentSection, setCurrentSubsection } from '../data/slice'; import { RequestStatus } from '../../data/constants'; +import { isUnitReadOnly } from '../../course-unit/data/utils'; import CardHeader from '../card-header/CardHeader'; import SortableItem from '../drag-helper/SortableItem'; import TitleLink from '../card-header/TitleLink'; @@ -57,6 +58,8 @@ const UnitCard = ({ discussionEnabled, } = unit; + const readOnly = isUnitReadOnly(unit); + // re-create actions object for customizations const actions = { ...unitActions }; // add actions to control display of move up & down menu buton. @@ -175,7 +178,7 @@ const UnitCard = ({ isFormOpen={isFormOpen} closeForm={closeForm} onEditSubmit={handleEditSubmit} - isDisabledEditField={savingStatus === RequestStatus.IN_PROGRESS} + isDisabledEditField={readOnly || savingStatus === RequestStatus.IN_PROGRESS} onClickDuplicate={onDuplicateSubmit} titleComponent={titleComponent} namePrefix={namePrefix} diff --git a/src/course-unit/CourseUnit.jsx b/src/course-unit/CourseUnit.jsx index bc97c48548..5aae700e5b 100644 --- a/src/course-unit/CourseUnit.jsx +++ b/src/course-unit/CourseUnit.jsx @@ -40,6 +40,7 @@ const CourseUnit = ({ courseId }) => { const { blockId } = useParams(); const intl = useIntl(); const { + courseUnit, isLoading, sequenceId, unitTitle, @@ -75,6 +76,8 @@ const CourseUnit = ({ courseId }) => { } = useCourseUnit({ courseId, blockId }); const layoutGrid = useLayoutGrid(unitCategory, isUnitLibraryType); + const readOnly = !!courseUnit.readOnly; + useEffect(() => { document.title = getPageHeadTitle('', unitTitle); }, [unitTitle]); @@ -195,14 +198,16 @@ const CourseUnit = ({ courseId }) => { courseVerticalChildren={courseVerticalChildren.children} handleConfigureSubmit={handleConfigureSubmit} /> - - {showPasteXBlock && canPasteComponent && isUnitVerticalType && ( + {!readOnly && ( + + )} + {!readOnly && showPasteXBlock && canPasteComponent && isUnitVerticalType && ( { blockId={blockId} unitTitle={unitTitle} xBlocks={courseVerticalChildren.children} + readOnly={readOnly} /> )} {isSplitTestType && ( diff --git a/src/course-unit/CourseUnit.test.jsx b/src/course-unit/CourseUnit.test.jsx index 2f128c0014..bd204ddf28 100644 --- a/src/course-unit/CourseUnit.test.jsx +++ b/src/course-unit/CourseUnit.test.jsx @@ -2195,4 +2195,51 @@ describe('', () => { .toHaveBeenCalledWith(`/course/${courseId}/editor/html/${targetBlockId}`, { replace: true }); }); }); + + it('renders units from libraries with some components read-only', async () => { + setConfig({ + ...getConfig(), + ENABLE_TAGGING_TAXONOMY_PAGES: 'true', + }); + render(); + + axiosMock + .onGet(getCourseUnitApiUrl(courseId)) + .reply(200, { + ...courseUnitIndexMock, + upstreamInfo: { + upstreamRef: 'lct:org:lib:unit:unit-1', + }, + }); + await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); + + // Disable the "Edit" button + const unitHeaderTitle = screen.getByTestId('unit-header-title'); + const editButton = within(unitHeaderTitle).getByRole( + 'button', + { name: 'Edit' }, + ); + expect(editButton).toBeInTheDocument(); + expect(editButton).toBeDisabled(); + + // The "Publish" button should still be enabled + const courseUnitSidebar = screen.getByTestId('course-unit-sidebar'); + const publishButton = within(courseUnitSidebar).getByRole( + 'button', + { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }, + ); + expect(publishButton).toBeInTheDocument(); + expect(publishButton).toBeEnabled(); + + // Disable the "Manage Tags" button + const manageTagsButton = screen.getByRole( + 'button', + { name: tagsDrawerMessages.manageTagsButton.defaultMessage }, + ); + expect(manageTagsButton).toBeInTheDocument(); + expect(manageTagsButton).toBeDisabled(); + + // Does not render the "Add Components" section + expect(screen.queryByText(addComponentMessages.title.defaultMessage)).not.toBeInTheDocument(); + }); }); diff --git a/src/course-unit/add-component/AddComponent.jsx b/src/course-unit/add-component/AddComponent.jsx index be5ec6ec05..3c44f743fb 100644 --- a/src/course-unit/add-component/AddComponent.jsx +++ b/src/course-unit/add-component/AddComponent.jsx @@ -208,10 +208,6 @@ const AddComponent = ({ return null; }; -AddComponent.defaultProps = { - addComponentTemplateData: {}, -}; - AddComponent.propTypes = { isSplitTestType: PropTypes.bool.isRequired, isUnitVerticalType: PropTypes.bool.isRequired, diff --git a/src/course-unit/data/thunk.js b/src/course-unit/data/thunk.js index bdf2e44024..a0c1dc54ec 100644 --- a/src/course-unit/data/thunk.js +++ b/src/course-unit/data/thunk.js @@ -38,7 +38,7 @@ import { updateCourseOutlineInfoLoadingStatus, updateMovedXBlockParams, } from './slice'; -import { getNotificationMessage } from './utils'; +import { getNotificationMessage, isUnitReadOnly } from './utils'; export function fetchCourseUnitQuery(courseId) { return async (dispatch) => { @@ -46,6 +46,8 @@ export function fetchCourseUnitQuery(courseId) { try { const courseUnit = await getCourseUnitData(courseId); + courseUnit.readOnly = isUnitReadOnly(courseUnit); + dispatch(fetchCourseItemSuccess(courseUnit)); dispatch(updateLoadingCourseUnitStatus({ status: RequestStatus.SUCCESSFUL })); return true; diff --git a/src/course-unit/data/utils.js b/src/course-unit/data/utils.js index 0b28805297..891021debd 100644 --- a/src/course-unit/data/utils.js +++ b/src/course-unit/data/utils.js @@ -84,3 +84,15 @@ export const updateXBlockBlockIdToId = (data) => { return updatedData; }; + +/** + * Returns whether the given Unit should be read-only. + * + * Units sourced from libraries are read-only (temporary, for Teak). + * + * @param {object} unit - uses the 'upstreamInfo' object if found. + * @returns {boolean} True if readOnly, False if editable. + */ +export const isUnitReadOnly = ({ upstreamInfo }) => ( + upstreamInfo && upstreamInfo.upstreamRef && upstreamInfo.upstreamRef.startsWith('lct:') +); diff --git a/src/course-unit/header-title/HeaderTitle.jsx b/src/course-unit/header-title/HeaderTitle.jsx index f7955b8636..1c318c207a 100644 --- a/src/course-unit/header-title/HeaderTitle.jsx +++ b/src/course-unit/header-title/HeaderTitle.jsx @@ -34,6 +34,8 @@ const HeaderTitle = ({ COURSE_BLOCK_NAMES.component.id, ].includes(currentItemData.category); + const readOnly = !!currentItemData.readOnly; + const onConfigureSubmit = (...arg) => { handleConfigureSubmit(currentItemData.id, ...arg, closeConfigureModal); }; @@ -80,12 +82,14 @@ const HeaderTitle = ({ className="ml-1 flex-shrink-0" iconAs={EditIcon} onClick={handleTitleEdit} + disabled={readOnly} /> ', () => { expect(getByRole('textbox', { name: messages.ariaLabelButtonEdit.defaultMessage })).toBeInTheDocument(); expect(getByRole('textbox', { name: messages.ariaLabelButtonEdit.defaultMessage })).toHaveValue(unitTitle); - expect(getByRole('button', { name: messages.altButtonEdit.defaultMessage })).toBeInTheDocument(); - expect(getByRole('button', { name: messages.altButtonSettings.defaultMessage })).toBeInTheDocument(); + expect(getByRole('button', { name: messages.altButtonEdit.defaultMessage })).toBeEnabled(); + expect(getByRole('button', { name: messages.altButtonSettings.defaultMessage })).toBeEnabled(); + }); + + it('Units sourced from upstream show a disabled edit form and config menu', async () => { + // Override mock unit with one sourced from an upstream library + axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + axiosMock + .onGet(getCourseUnitApiUrl(blockId)) + .reply(200, { + ...courseUnitIndexMock, + upstreamInfo: { + upstreamRef: 'lct:org:lib:unit:unit-1', + }, + }); + await executeThunk(fetchCourseUnitQuery(blockId), store.dispatch); + + const { getByRole } = renderComponent(); + + expect(getByRole('button', { name: messages.altButtonEdit.defaultMessage })).toBeDisabled(); + expect(getByRole('button', { name: messages.altButtonSettings.defaultMessage })).toBeDisabled(); }); it('calls toggle edit title form by clicking on Edit button', () => { diff --git a/src/course-unit/sidebar/PublishControls.jsx b/src/course-unit/sidebar/PublishControls.tsx similarity index 92% rename from src/course-unit/sidebar/PublishControls.jsx rename to src/course-unit/sidebar/PublishControls.tsx index 2a6ea83995..2b6372548c 100644 --- a/src/course-unit/sidebar/PublishControls.jsx +++ b/src/course-unit/sidebar/PublishControls.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useToggle } from '@openedx/paragon'; import { InfoOutline as InfoOutlineIcon } from '@openedx/paragon/icons'; @@ -12,14 +11,19 @@ import { getCourseUnitData } from '../data/selectors'; import messages from './messages'; import ModalNotification from '../../generic/modal-notification'; -const PublishControls = ({ blockId }) => { +interface PublishControlsProps { + blockId?: string, +} + +const PublishControls = ({ blockId }: PublishControlsProps) => { + const unitData = useSelector(getCourseUnitData); const { title, locationId, releaseLabel, visibilityState, visibleToStaffOnly, - } = useCourseUnitData(useSelector(getCourseUnitData)); + } = useCourseUnitData(unitData); const intl = useIntl(); const { sendMessageToIframe } = useIframe(); @@ -90,12 +94,4 @@ const PublishControls = ({ blockId }) => { ); }; -PublishControls.propTypes = { - blockId: PropTypes.string, -}; - -PublishControls.defaultProps = { - blockId: null, -}; - export default PublishControls; diff --git a/src/course-unit/sidebar/components/sidebar-footer/ActionButtons.jsx b/src/course-unit/sidebar/components/sidebar-footer/ActionButtons.tsx similarity index 81% rename from src/course-unit/sidebar/components/sidebar-footer/ActionButtons.jsx rename to src/course-unit/sidebar/components/sidebar-footer/ActionButtons.tsx index 645651a271..8bedc41c8b 100644 --- a/src/course-unit/sidebar/components/sidebar-footer/ActionButtons.jsx +++ b/src/course-unit/sidebar/components/sidebar-footer/ActionButtons.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { Button } from '@openedx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; @@ -8,7 +7,15 @@ import { getCanEdit, getCourseUnitData } from '../../../data/selectors'; import { useClipboard } from '../../../../generic/clipboard'; import messages from '../../messages'; -const ActionButtons = ({ openDiscardModal, handlePublishing }) => { +interface ActionButtonsProps { + openDiscardModal: () => void, + handlePublishing: () => void, +} + +const ActionButtons = ({ + openDiscardModal, + handlePublishing, +}: ActionButtonsProps) => { const intl = useIntl(); const { id, @@ -22,7 +29,12 @@ const ActionButtons = ({ openDiscardModal, handlePublishing }) => { return ( <> {(!published || hasChanges) && ( - )} @@ -52,9 +64,4 @@ const ActionButtons = ({ openDiscardModal, handlePublishing }) => { ); }; -ActionButtons.propTypes = { - openDiscardModal: PropTypes.func.isRequired, - handlePublishing: PropTypes.func.isRequired, -}; - export default ActionButtons; diff --git a/src/course-unit/sidebar/components/sidebar-footer/UnitVisibilityInfo.jsx b/src/course-unit/sidebar/components/sidebar-footer/UnitVisibilityInfo.tsx similarity index 89% rename from src/course-unit/sidebar/components/sidebar-footer/UnitVisibilityInfo.jsx rename to src/course-unit/sidebar/components/sidebar-footer/UnitVisibilityInfo.tsx index b4dc35b568..e10edcdfe6 100644 --- a/src/course-unit/sidebar/components/sidebar-footer/UnitVisibilityInfo.jsx +++ b/src/course-unit/sidebar/components/sidebar-footer/UnitVisibilityInfo.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { Form } from '@openedx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; @@ -10,7 +9,15 @@ import { PUBLISH_TYPES } from '../../../constants'; import { getVisibilityTitle } from '../../utils'; import messages from '../../messages'; -const UnitVisibilityInfo = ({ openVisibleModal, visibleToStaffOnly }) => { +interface UnitVisibilityInfoProps { + openVisibleModal: () => void, + visibleToStaffOnly: boolean, +} + +const UnitVisibilityInfo = ({ + openVisibleModal, + visibleToStaffOnly, +}: UnitVisibilityInfoProps) => { const intl = useIntl(); const { blockId } = useParams(); const dispatch = useDispatch(); @@ -59,9 +66,4 @@ const UnitVisibilityInfo = ({ openVisibleModal, visibleToStaffOnly }) => { ); }; -UnitVisibilityInfo.propTypes = { - openVisibleModal: PropTypes.func.isRequired, - visibleToStaffOnly: PropTypes.bool.isRequired, -}; - export default UnitVisibilityInfo; diff --git a/src/course-unit/sidebar/components/sidebar-footer/index.jsx b/src/course-unit/sidebar/components/sidebar-footer/index.tsx similarity index 74% rename from src/course-unit/sidebar/components/sidebar-footer/index.jsx rename to src/course-unit/sidebar/components/sidebar-footer/index.tsx index 62af6c672b..f05591bb07 100644 --- a/src/course-unit/sidebar/components/sidebar-footer/index.jsx +++ b/src/course-unit/sidebar/components/sidebar-footer/index.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { Card, Stack } from '@openedx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; @@ -6,14 +5,23 @@ import messages from '../../messages'; import UnitVisibilityInfo from './UnitVisibilityInfo'; import ActionButtons from './ActionButtons'; +interface SidebarFooterProps { + locationId?: string, + displayUnitLocation?: boolean, + openDiscardModal: () => void, + openVisibleModal: () => void, + handlePublishing: () => void, + visibleToStaffOnly: boolean, +} + const SidebarFooter = ({ locationId, openVisibleModal, handlePublishing, openDiscardModal, visibleToStaffOnly, - displayUnitLocation, -}) => { + displayUnitLocation = false, +}: SidebarFooterProps) => { const intl = useIntl(); return ( @@ -40,18 +48,4 @@ const SidebarFooter = ({ ); }; -SidebarFooter.propTypes = { - locationId: PropTypes.string, - displayUnitLocation: PropTypes.bool, - openDiscardModal: PropTypes.func, - openVisibleModal: PropTypes.func, - handlePublishing: PropTypes.func, - visibleToStaffOnly: PropTypes.bool.isRequired, -}; - -SidebarFooter.defaultProps = { - displayUnitLocation: false, - locationId: null, -}; - export default SidebarFooter; diff --git a/src/generic/configure-modal/ConfigureModal.jsx b/src/generic/configure-modal/ConfigureModal.jsx index 76b2bd603a..915ba5d19b 100644 --- a/src/generic/configure-modal/ConfigureModal.jsx +++ b/src/generic/configure-modal/ConfigureModal.jsx @@ -26,8 +26,8 @@ const ConfigureModal = ({ onClose, onConfigureSubmit, currentItemData, - enableProctoredExams, - isXBlockComponent, + enableProctoredExams = false, + isXBlockComponent = false, isSelfPaced, }) => { const intl = useIntl(); @@ -320,11 +320,6 @@ const ConfigureModal = ({ ); }; -ConfigureModal.defaultProps = { - isXBlockComponent: false, - enableProctoredExams: false, -}; - ConfigureModal.propTypes = { isOpen: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, diff --git a/src/plugin-slots/CourseAuthoringUnitSidebarSlot/README.md b/src/plugin-slots/CourseAuthoringUnitSidebarSlot/README.md index b8fe123066..ee00e7ba16 100644 --- a/src/plugin-slots/CourseAuthoringUnitSidebarSlot/README.md +++ b/src/plugin-slots/CourseAuthoringUnitSidebarSlot/README.md @@ -11,6 +11,7 @@ * `blockId` - String. The usage id of the current unit being viewed / edited. * `unitTitle` - String. The name of the current unit being viewed / edited. * `xBlocks` - Array of Objects. List of XBlocks in the Unit. Object structure defined in `index.tsx`. +* `readOnly` - Boolean. True if the user should not be able to edit the contents of the unit. ## Description diff --git a/src/plugin-slots/CourseAuthoringUnitSidebarSlot/index.tsx b/src/plugin-slots/CourseAuthoringUnitSidebarSlot/index.tsx index af8f97d670..0bf6de14ca 100644 --- a/src/plugin-slots/CourseAuthoringUnitSidebarSlot/index.tsx +++ b/src/plugin-slots/CourseAuthoringUnitSidebarSlot/index.tsx @@ -11,13 +11,14 @@ export const CourseAuthoringUnitSidebarSlot = ( courseId, unitTitle, xBlocks, + readOnly, }: CourseAuthoringUnitSidebarSlotProps, ) => ( @@ -25,7 +26,7 @@ export const CourseAuthoringUnitSidebarSlot = ( {getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true' && ( - + )} @@ -45,4 +46,5 @@ interface CourseAuthoringUnitSidebarSlotProps { courseId: string; unitTitle: string; xBlocks: XBlock[]; + readOnly: boolean; }