diff --git a/src/course-outline/card-header/CardHeader.jsx b/src/course-outline/card-header/CardHeader.jsx index 1524b3fb17..4e7cae4e43 100644 --- a/src/course-outline/card-header/CardHeader.jsx +++ b/src/course-outline/card-header/CardHeader.jsx @@ -27,6 +27,7 @@ const CardHeader = ({ hasChanges, onClickPublish, onClickConfigure, + onClickManageTags, onClickMenuButton, onClickEdit, isFormOpen, @@ -162,6 +163,15 @@ const CardHeader = ({ > {intl.formatMessage(messages.menuConfigure)} + {onClickManageTags && ( + + {intl.formatMessage(messages.menuManageTags)} + + )} + {isVertical && enableCopyPasteUnits && ( {intl.formatMessage(messages.menuCopy)} @@ -218,6 +228,7 @@ CardHeader.defaultProps = { discussionEnabled: false, discussionsSettings: {}, parentInfo: {}, + onClickManageTags: null, }; CardHeader.propTypes = { @@ -227,6 +238,7 @@ CardHeader.propTypes = { hasChanges: PropTypes.bool.isRequired, onClickPublish: PropTypes.func.isRequired, onClickConfigure: PropTypes.func.isRequired, + onClickManageTags: PropTypes.func, onClickMenuButton: PropTypes.func.isRequired, onClickEdit: PropTypes.func.isRequired, isFormOpen: PropTypes.bool.isRequired, diff --git a/src/course-outline/card-header/CardHeader.test.jsx b/src/course-outline/card-header/CardHeader.test.jsx index 1a666b6614..35ce66d599 100644 --- a/src/course-outline/card-header/CardHeader.test.jsx +++ b/src/course-outline/card-header/CardHeader.test.jsx @@ -1,6 +1,6 @@ import { MemoryRouter } from 'react-router-dom'; import { - act, render, fireEvent, waitFor, + act, render, fireEvent, waitFor, screen, } from '@testing-library/react'; import { IntlProvider } from '@edx/frontend-platform/i18n'; @@ -18,6 +18,7 @@ const onClickDuplicateMock = jest.fn(); const onClickConfigureMock = jest.fn(); const onClickMoveUpMock = jest.fn(); const onClickMoveDownMock = jest.fn(); +const onClickManageTagsMock = jest.fn(); const closeFormMock = jest.fn(); const cardHeaderProps = { @@ -28,6 +29,7 @@ const cardHeaderProps = { onClickMenuButton: onClickMenuButtonMock, onClickPublish: onClickPublishMock, onClickEdit: onClickEditMock, + onClickManageTags: onClickManageTagsMock, isFormOpen: false, onEditSubmit: jest.fn(), closeForm: closeFormMock, @@ -168,6 +170,16 @@ describe('', () => { expect(onClickPublishMock).toHaveBeenCalled(); }); + it('calls onClickManageTags when the menu is clicked', async () => { + renderComponent(); + const menuButton = await screen.findByTestId('subsection-card-header__menu-button'); + fireEvent.click(menuButton); + + const manageTagsMenuItem = await screen.findByText(messages.menuManageTags.defaultMessage); + await act(async () => fireEvent.click(manageTagsMenuItem)); + expect(onClickManageTagsMock).toHaveBeenCalled(); + }); + it('calls onClickEdit when the button is clicked', async () => { const { findByTestId } = renderComponent(); diff --git a/src/course-outline/card-header/messages.js b/src/course-outline/card-header/messages.js index d9f250970d..410443d695 100644 --- a/src/course-outline/card-header/messages.js +++ b/src/course-outline/card-header/messages.js @@ -73,6 +73,10 @@ const messages = defineMessages({ id: 'course-authoring.course-outline.card.badge.discussionEnabled', defaultMessage: 'Discussions enabled', }, + menuManageTags: { + id: 'course-authoring.course-outline.card.menu.manageTags', + defaultMessage: 'Manage tags', + }, }); export default messages; diff --git a/src/course-outline/unit-card/UnitCard.jsx b/src/course-outline/unit-card/UnitCard.jsx index f2cb4ac3e9..1ab5a97a5c 100644 --- a/src/course-outline/unit-card/UnitCard.jsx +++ b/src/course-outline/unit-card/UnitCard.jsx @@ -1,7 +1,7 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { useDispatch } from 'react-redux'; -import { useToggle } from '@openedx/paragon'; +import { useToggle, Sheet } from '@openedx/paragon'; import { setCurrentItem, setCurrentSection, setCurrentSubsection } from '../data/slice'; import { RequestStatus } from '../../data/constants'; @@ -10,6 +10,7 @@ import ConditionalSortableElement from '../drag-helper/ConditionalSortableElemen import TitleLink from '../card-header/TitleLink'; import XBlockStatus from '../xblock-status/XBlockStatus'; import { getItemStatus, getItemStatusBorder, scrollToElement } from '../utils'; +import { ContentTagsDrawer } from '../../content-tags-drawer'; const UnitCard = ({ unit, @@ -34,6 +35,7 @@ const UnitCard = ({ const dispatch = useDispatch(); const [isFormOpen, openForm, closeForm] = useToggle(false); const namePrefix = 'unit'; + const [showManageTags, setShowManageTags] = useState(false); const { id, @@ -122,55 +124,68 @@ const UnitCard = ({ const isDraggable = actions.draggable && (actions.allowMoveUp || actions.allowMoveDown); return ( - -
+ - -
- + setShowManageTags(true)} + onClickEdit={openForm} + onClickDelete={onOpenDeleteModal} + onClickMoveUp={handleUnitMoveUp} + onClickMoveDown={handleUnitMoveDown} + isFormOpen={isFormOpen} + closeForm={closeForm} + onEditSubmit={handleEditSubmit} + isDisabledEditField={savingStatus === RequestStatus.IN_PROGRESS} + onClickDuplicate={onDuplicateSubmit} + titleComponent={titleComponent} + namePrefix={namePrefix} + actions={actions} + isVertical + enableCopyPasteUnits={enableCopyPasteUnits} + onClickCopy={handleCopyClick} + discussionEnabled={discussionEnabled} + discussionsSettings={discussionsSettings} + parentInfo={parentInfo} /> +
+ +
-
-
+ + setShowManageTags(false)} + > + setShowManageTags(false)} + /> + + ); };