diff --git a/src/course-outline/CourseOutline.jsx b/src/course-outline/CourseOutline.jsx index b5f3f3b0a8..df52bd7361 100644 --- a/src/course-outline/CourseOutline.jsx +++ b/src/course-outline/CourseOutline.jsx @@ -45,12 +45,12 @@ import HighlightsModal from './highlights-modal/HighlightsModal'; import EmptyPlaceholder from './empty-placeholder/EmptyPlaceholder'; import PublishModal from './publish-modal/PublishModal'; import PageAlerts from './page-alerts/PageAlerts'; -import DraggableList from '../generic/drag-helper/DraggableList'; +import DraggableList from './drag-helper/DraggableList'; import { canMoveSection, possibleUnitMoves, possibleSubsectionMoves, -} from '../generic/drag-helper/utils'; +} from './drag-helper/utils'; import { useCourseOutline } from './hooks'; import messages from './messages'; import { getTagsExportFile } from './data/api'; diff --git a/src/course-outline/CourseOutline.scss b/src/course-outline/CourseOutline.scss index 056b34a8eb..101d61ff51 100644 --- a/src/course-outline/CourseOutline.scss +++ b/src/course-outline/CourseOutline.scss @@ -7,3 +7,4 @@ @import "./highlights-modal/HighlightsModal"; @import "./publish-modal/PublishModal"; @import "./xblock-status/XBlockStatus"; +@import "./drag-helper/SortableItem"; diff --git a/src/course-outline/CourseOutline.test.jsx b/src/course-outline/CourseOutline.test.jsx index 05ff962057..2ca68dc44a 100644 --- a/src/course-outline/CourseOutline.test.jsx +++ b/src/course-outline/CourseOutline.test.jsx @@ -59,7 +59,7 @@ import { moveUnitOver, moveSubsection, moveUnit, -} from '../generic/drag-helper/utils'; +} from './drag-helper/utils'; let axiosMock; let store; diff --git a/src/generic/drag-helper/DragContextProvider.jsx b/src/course-outline/drag-helper/DragContextProvider.jsx similarity index 100% rename from src/generic/drag-helper/DragContextProvider.jsx rename to src/course-outline/drag-helper/DragContextProvider.jsx diff --git a/src/generic/drag-helper/DraggableList.jsx b/src/course-outline/drag-helper/DraggableList.jsx similarity index 100% rename from src/generic/drag-helper/DraggableList.jsx rename to src/course-outline/drag-helper/DraggableList.jsx diff --git a/src/generic/drag-helper/SortableItem.jsx b/src/course-outline/drag-helper/SortableItem.jsx similarity index 100% rename from src/generic/drag-helper/SortableItem.jsx rename to src/course-outline/drag-helper/SortableItem.jsx diff --git a/src/generic/drag-helper/SortableItem.scss b/src/course-outline/drag-helper/SortableItem.scss similarity index 100% rename from src/generic/drag-helper/SortableItem.scss rename to src/course-outline/drag-helper/SortableItem.scss diff --git a/src/editors/sharedComponents/DraggableList/messages.js b/src/course-outline/drag-helper/messages.js similarity index 100% rename from src/editors/sharedComponents/DraggableList/messages.js rename to src/course-outline/drag-helper/messages.js diff --git a/src/generic/drag-helper/utils.js b/src/course-outline/drag-helper/utils.js similarity index 100% rename from src/generic/drag-helper/utils.js rename to src/course-outline/drag-helper/utils.js diff --git a/src/course-outline/section-card/SectionCard.jsx b/src/course-outline/section-card/SectionCard.jsx index f8d11a4cd0..5d3b5fbb5a 100644 --- a/src/course-outline/section-card/SectionCard.jsx +++ b/src/course-outline/section-card/SectionCard.jsx @@ -13,8 +13,8 @@ import classNames from 'classnames'; import { setCurrentItem, setCurrentSection } from '../data/slice'; import { RequestStatus } from '../../data/constants'; import CardHeader from '../card-header/CardHeader'; -import SortableItem from '../../generic/drag-helper/SortableItem'; -import { DragContext } from '../../generic/drag-helper/DragContextProvider'; +import SortableItem from '../drag-helper/SortableItem'; +import { DragContext } from '../drag-helper/DragContextProvider'; import TitleButton from '../card-header/TitleButton'; import XBlockStatus from '../xblock-status/XBlockStatus'; import { getItemStatus, getItemStatusBorder, scrollToElement } from '../utils'; diff --git a/src/course-outline/subsection-card/SubsectionCard.jsx b/src/course-outline/subsection-card/SubsectionCard.jsx index 1d5c7b0367..86d35231b3 100644 --- a/src/course-outline/subsection-card/SubsectionCard.jsx +++ b/src/course-outline/subsection-card/SubsectionCard.jsx @@ -15,8 +15,8 @@ import CourseOutlineSubsectionCardExtraActionsSlot from '../../plugin-slots/Cour import { setCurrentItem, setCurrentSection, setCurrentSubsection } from '../data/slice'; import { RequestStatus } from '../../data/constants'; import CardHeader from '../card-header/CardHeader'; -import SortableItem from '../../generic/drag-helper/SortableItem'; -import { DragContext } from '../../generic/drag-helper/DragContextProvider'; +import SortableItem from '../drag-helper/SortableItem'; +import { DragContext } from '../drag-helper/DragContextProvider'; import { useClipboard, PasteComponent } from '../../generic/clipboard'; import TitleButton from '../card-header/TitleButton'; import XBlockStatus from '../xblock-status/XBlockStatus'; diff --git a/src/course-outline/unit-card/UnitCard.jsx b/src/course-outline/unit-card/UnitCard.jsx index 7d17d24c53..00ac4cf50d 100644 --- a/src/course-outline/unit-card/UnitCard.jsx +++ b/src/course-outline/unit-card/UnitCard.jsx @@ -10,7 +10,7 @@ import CourseOutlineUnitCardExtraActionsSlot from '../../plugin-slots/CourseOutl import { setCurrentItem, setCurrentSection, setCurrentSubsection } from '../data/slice'; import { RequestStatus } from '../../data/constants'; import CardHeader from '../card-header/CardHeader'; -import SortableItem from '../../generic/drag-helper/SortableItem'; +import SortableItem from '../drag-helper/SortableItem'; import TitleLink from '../card-header/TitleLink'; import XBlockStatus from '../xblock-status/XBlockStatus'; import { getItemStatus, getItemStatusBorder, scrollToElement } from '../utils'; diff --git a/src/custom-pages/CustomPages.jsx b/src/custom-pages/CustomPages.jsx index ad69e670f2..d2c5e7e317 100644 --- a/src/custom-pages/CustomPages.jsx +++ b/src/custom-pages/CustomPages.jsx @@ -21,7 +21,7 @@ import { } from '@openedx/paragon'; import { Add, SpinnerSimple } from '@openedx/paragon/icons'; import Placeholder from '../editors/Placeholder'; -import DraggableList, { SortableItem } from '../editors/sharedComponents/DraggableList'; +import DraggableList, { SortableItem } from '../generic/DraggableList'; import ErrorAlert from '../editors/sharedComponents/ErrorAlerts/ErrorAlert'; import { RequestStatus } from '../data/constants'; diff --git a/src/editors/sharedComponents/DraggableList/DraggableList.jsx b/src/generic/DraggableList/DraggableList.jsx similarity index 70% rename from src/editors/sharedComponents/DraggableList/DraggableList.jsx rename to src/generic/DraggableList/DraggableList.jsx index 6946d11770..1515f29a06 100644 --- a/src/editors/sharedComponents/DraggableList/DraggableList.jsx +++ b/src/generic/DraggableList/DraggableList.jsx @@ -1,5 +1,6 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; +import { createPortal } from 'react-dom'; import { DndContext, @@ -8,6 +9,7 @@ import { PointerSensor, useSensor, useSensors, + DragOverlay, } from '@dnd-kit/core'; import { arrayMove, @@ -22,6 +24,9 @@ const DraggableList = ({ setState, updateOrder, children, + renderOverlay, + activeId, + setActiveId, }) => { const sensors = useSensors( useSensor(PointerSensor), @@ -30,7 +35,7 @@ const DraggableList = ({ }), ); - const handleDragEnd = (event) => { + const handleDragEnd = useCallback((event) => { const { active, over } = event; if (active.id !== over.id) { let updatedArray; @@ -44,13 +49,19 @@ const DraggableList = ({ }); updateOrder()(updatedArray); } - }; + setActiveId?.(null); + }, [updateOrder, setActiveId]); + + const handleDragStart = useCallback((event) => { + setActiveId?.(event.active.id); + }, [setActiveId]); return ( {children} + {renderOverlay && createPortal( + + {renderOverlay(activeId)} + , + document.body, + )} ); }; +DraggableList.defaultProps = { + renderOverlay: undefined, + activeId: null, + setActiveId: () => {}, +}; + DraggableList.propTypes = { itemList: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired, @@ -70,6 +93,9 @@ DraggableList.propTypes = { setState: PropTypes.func.isRequired, updateOrder: PropTypes.func.isRequired, children: PropTypes.node.isRequired, + renderOverlay: PropTypes.func, + activeId: PropTypes.string, + setActiveId: PropTypes.func, }; export default DraggableList; diff --git a/src/editors/sharedComponents/DraggableList/SortableItem.jsx b/src/generic/DraggableList/SortableItem.jsx similarity index 93% rename from src/editors/sharedComponents/DraggableList/SortableItem.jsx rename to src/generic/DraggableList/SortableItem.jsx index 01816624af..b86563cb9c 100644 --- a/src/editors/sharedComponents/DraggableList/SortableItem.jsx +++ b/src/generic/DraggableList/SortableItem.jsx @@ -17,6 +17,7 @@ const SortableItem = ({ children, isClickable, onClick, + disabled, // injected intl, }) => { @@ -31,6 +32,9 @@ const SortableItem = ({ } = useSortable({ id, animateLayoutChanges: () => false, + disabled: { + draggable: disabled, + }, }); const style = { @@ -52,6 +56,7 @@ const SortableItem = ({ > {actions} + {!disabled && ( + )} {children} @@ -76,6 +82,7 @@ SortableItem.defaultProps = { actionStyle: null, isClickable: false, onClick: null, + disabled: false, }; SortableItem.propTypes = { id: PropTypes.string.isRequired, @@ -85,6 +92,7 @@ SortableItem.propTypes = { componentStyle: PropTypes.shape({}), isClickable: PropTypes.bool, onClick: PropTypes.func, + disabled: PropTypes.bool, // injected intl: intlShape.isRequired, }; diff --git a/src/editors/sharedComponents/DraggableList/index.jsx b/src/generic/DraggableList/index.jsx similarity index 100% rename from src/editors/sharedComponents/DraggableList/index.jsx rename to src/generic/DraggableList/index.jsx diff --git a/src/generic/drag-helper/messages.js b/src/generic/DraggableList/messages.js similarity index 100% rename from src/generic/drag-helper/messages.js rename to src/generic/DraggableList/messages.js diff --git a/src/generic/styles.scss b/src/generic/styles.scss index 2ec14a72b6..00ef459221 100644 --- a/src/generic/styles.scss +++ b/src/generic/styles.scss @@ -11,6 +11,5 @@ @import "./tag-count/TagCount"; @import "./modal-dropzone/ModalDropzone"; @import "./configure-modal/ConfigureModal"; -@import "./drag-helper/SortableItem"; @import "./block-type-utils"; @import "./modal-iframe" diff --git a/src/library-authoring/data/api.test.ts b/src/library-authoring/data/api.test.ts index e75a2b4b1a..37ebc4105a 100644 --- a/src/library-authoring/data/api.test.ts +++ b/src/library-authoring/data/api.test.ts @@ -126,4 +126,15 @@ describe('library data API', () => { await api.addComponentsToContainer(containerId, [componentId]); expect(axiosMock.history.post[0].url).toEqual(url); }); + + it('should update container children', async () => { + const { axiosMock } = initializeMocks(); + const containerId = 'lct:org:lib1'; + const url = api.getLibraryContainerChildrenApiUrl(containerId); + + axiosMock.onPatch(url).reply(200); + + await api.updateLibraryContainerChildren(containerId, ['test']); + expect(axiosMock.history.patch[0].url).toEqual(url); + }); }); diff --git a/src/library-authoring/data/api.ts b/src/library-authoring/data/api.ts index b4a7bca673..1eabb381ed 100644 --- a/src/library-authoring/data/api.ts +++ b/src/library-authoring/data/api.ts @@ -670,3 +670,17 @@ export async function updateContainerCollections(containerId: string, collection collection_keys: collectionKeys, }); } + +/** + * Update library container's children. + */ +export async function updateLibraryContainerChildren( + containerId: string, + children: string[], +): Promise { + const { data } = await getAuthenticatedHttpClient().patch( + getLibraryContainerChildrenApiUrl(containerId), + { usage_keys: children }, + ); + return camelCaseObject(data); +} diff --git a/src/library-authoring/data/apiHooks.test.tsx b/src/library-authoring/data/apiHooks.test.tsx index 57e0a8fa73..6a1fd3dc50 100644 --- a/src/library-authoring/data/apiHooks.test.tsx +++ b/src/library-authoring/data/apiHooks.test.tsx @@ -29,6 +29,7 @@ import { useRestoreContainer, useContainerChildren, useAddComponentsToContainer, + useUpdateContainerChildren, } from './apiHooks'; let axiosMock; @@ -266,4 +267,24 @@ describe('library api hooks', () => { expect(axiosMock.history.post[0].url).toEqual(url); }); + + it('should update container children', async () => { + const containerId = 'lct:org:lib1'; + const url = getLibraryContainerChildrenApiUrl(containerId); + + axiosMock.onPatch(url).reply(200); + const { result } = renderHook(() => useUpdateContainerChildren(containerId), { wrapper }); + await result.current.mutateAsync([]); + await waitFor(() => { + expect(axiosMock.history.patch[0].url).toEqual(url); + }); + }); + + it('should not attempt request if containerId is not defined', async () => { + const { result } = renderHook(() => useUpdateContainerChildren(), { wrapper }); + await result.current.mutateAsync([]); + await waitFor(() => { + expect(axiosMock.history.patch.length).toEqual(0); + }); + }); }); diff --git a/src/library-authoring/data/apiHooks.ts b/src/library-authoring/data/apiHooks.ts index e20671a2b2..b52b597e35 100644 --- a/src/library-authoring/data/apiHooks.ts +++ b/src/library-authoring/data/apiHooks.ts @@ -56,6 +56,7 @@ import { restoreContainer, getLibraryContainerChildren, updateContainerCollections, + updateLibraryContainerChildren, } from './api'; import { VersionSpec } from '../LibraryBlock'; @@ -696,3 +697,28 @@ export const useUpdateContainerCollections = (containerId: string) => { }, }); }; + +/** + * Update container children + */ +export const useUpdateContainerChildren = (containerId?: string) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (usageKeys: string[]) => { + if (!containerId) { + return undefined; + } + return updateLibraryContainerChildren(containerId, usageKeys); + }, + onSettled: () => { + if (!containerId) { + return; + } + // NOTE: We invalidate the library query here because we need to update the library's + // container list. + const libraryId = getLibraryId(containerId); + queryClient.invalidateQueries({ predicate: (query) => libraryQueryPredicate(query, libraryId) }); + queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.container(containerId) }); + }, + }); +}; diff --git a/src/library-authoring/units/LibraryUnitBlocks.tsx b/src/library-authoring/units/LibraryUnitBlocks.tsx index f1873fb08b..16b0e13f98 100644 --- a/src/library-authoring/units/LibraryUnitBlocks.tsx +++ b/src/library-authoring/units/LibraryUnitBlocks.tsx @@ -1,14 +1,14 @@ import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; import { - ActionRow, Badge, Button, Icon, Stack, useToggle, + ActionRow, Badge, Button, Icon, IconButton, Stack, useToggle, } from '@openedx/paragon'; -import { Add, Description } from '@openedx/paragon/icons'; +import { Add, Description, DragIndicator } from '@openedx/paragon/icons'; import { useQueryClient } from '@tanstack/react-query'; import classNames from 'classnames'; -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { ContentTagsDrawerSheet } from '../../content-tags-drawer'; import { blockTypes } from '../../editors/data/constants/app'; -import DraggableList, { SortableItem } from '../../editors/sharedComponents/DraggableList'; +import DraggableList, { SortableItem } from '../../generic/DraggableList'; import ErrorAlert from '../../generic/alert-error'; import { getItemIcon } from '../../generic/block-type-utils'; @@ -20,11 +20,12 @@ import { useLibraryContext } from '../common/context/LibraryContext'; import { PickLibraryContentModal } from '../add-content'; import ComponentMenu from '../components'; import { LibraryBlockMetadata } from '../data/api'; -import { libraryAuthoringQueryKeys, useContainerChildren } from '../data/apiHooks'; +import { libraryAuthoringQueryKeys, useContainerChildren, useUpdateContainerChildren } from '../data/apiHooks'; import { LibraryBlock } from '../LibraryBlock'; import { useLibraryRoutes } from '../routes'; import messages from './messages'; import { useSidebarContext } from '../common/context/SidebarContext'; +import { ToastContext } from '../../generic/toast-context'; /** Components that need large min height in preview */ const LARGE_COMPONENTS = [ @@ -35,17 +36,53 @@ const LARGE_COMPONENTS = [ 'lti_consumer', ]; +interface BlockHeaderProps { + block: LibraryBlockMetadata; + onTagClick: () => void; +} + +/** Component header, split out to reuse in drag overlay */ +const BlockHeader = ({ block, onTagClick }: BlockHeaderProps) => ( + <> + + + {block.displayName} + + + + {block.hasUnpublishedChanges && ( + + + + + + + )} + + + + +); + interface LibraryUnitBlocksProps { + /** set to true if it is rendered as preview + * This disables drag and drop + */ preview?: boolean; } -export const LibraryUnitBlocks = ({ preview = false }: LibraryUnitBlocksProps) => { +export const LibraryUnitBlocks = ({ preview }: LibraryUnitBlocksProps) => { const intl = useIntl(); const [orderedBlocks, setOrderedBlocks] = useState([]); const [isManageTagsDrawerOpen, openManageTagsDrawer, closeManageTagsDrawer] = useToggle(false); const [isAddLibraryContentModalOpen, showAddLibraryContentModal, closeAddLibraryContentModal] = useToggle(); + const [hidePreviewFor, setHidePreviewFor] = useState(null); const { navigateTo } = useLibraryRoutes(); + const { showToast } = useContext(ToastContext); const { unitId, @@ -60,6 +97,7 @@ export const LibraryUnitBlocks = ({ preview = false }: LibraryUnitBlocksProps) = } = useSidebarContext(); const queryClient = useQueryClient(); + const orderMutator = useUpdateContainerChildren(unitId); const { data: blocks, isLoading, @@ -78,11 +116,14 @@ export const LibraryUnitBlocks = ({ preview = false }: LibraryUnitBlocksProps) = return ; } - /* istanbul ignore next */ - const handleReorder = () => (newOrder: LibraryBlockMetadata[]) => { - // eslint-disable-next-line no-console - console.log('LibraryUnitBlocks newOrder: ', newOrder); - // TODO: update order of components in unit + const handleReorder = () => async (newOrder: LibraryBlockMetadata[]) => { + const usageKeys = newOrder.map((o) => o.id); + try { + await orderMutator.mutateAsync(usageKeys); + showToast(intl.formatMessage(messages.orderUpdatedMsg)); + } catch (e) { + showToast(intl.formatMessage(messages.failedOrderUpdatedMsg)); + } }; const onTagSidebarClose = () => { @@ -103,44 +144,45 @@ export const LibraryUnitBlocks = ({ preview = false }: LibraryUnitBlocksProps) = return '200px'; }; + const renderOverlay = (activeId: string | null) => { + if (!activeId) { + return null; + } + const block = orderedBlocks?.find((val) => val.id === activeId); + if (!block) { + return null; + } + return ( + + + + + ); + }; + const renderedBlocks = orderedBlocks?.map((block) => ( - - - {block.displayName} - - - - {block.hasUnpublishedChanges && ( - - - - - - - )} - - - - - )} + actions={} actionStyle={{ borderRadius: '8px 8px 0px 0px', padding: '0.5rem 1rem', background: '#FBFAF9', borderBottom: 'solid 1px #E1DDDB', + outline: hidePreviewFor === block.id && '2px dashed gray', }} isClickable onClick={() => handleComponentSelection(block)} + disabled={preview} > + {hidePreviewFor !== block.id && (
+ )}
)); return (
- + {renderedBlocks} { !preview && ( diff --git a/src/library-authoring/units/LibraryUnitPage.test.tsx b/src/library-authoring/units/LibraryUnitPage.test.tsx index db1df10269..daa63b2301 100644 --- a/src/library-authoring/units/LibraryUnitPage.test.tsx +++ b/src/library-authoring/units/LibraryUnitPage.test.tsx @@ -1,6 +1,7 @@ import userEvent from '@testing-library/user-event'; import type MockAdapter from 'axios-mock-adapter'; +import { act } from 'react'; import { initializeMocks, fireEvent, @@ -9,7 +10,7 @@ import { waitFor, within, } from '../../testUtils'; -import { getLibraryContainerApiUrl } from '../data/api'; +import { getLibraryContainerApiUrl, getLibraryContainerChildrenApiUrl } from '../data/api'; import { mockContentLibrary, mockXBlockFields, @@ -20,12 +21,13 @@ import { import { mockContentSearchConfig, mockGetBlockTypes } from '../../search-manager/data/api.mock'; import { mockClipboardEmpty } from '../../generic/data/api.mock'; import LibraryLayout from '../LibraryLayout'; +import { ToastActionData } from '../../generic/toast-context'; const path = '/library/:libraryId/*'; const libraryTitle = mockContentLibrary.libraryData.title; let axiosMock: MockAdapter; -let mockShowToast: (message: string) => void; +let mockShowToast: (message: string, action?: ToastActionData | undefined) => void; mockClipboardEmpty.applyMock(); mockGetContainerMetadata.applyMock(); @@ -36,6 +38,16 @@ mockContentLibrary.applyMock(); mockXBlockFields.applyMock(); mockLibraryBlockMetadata.applyMock(); +const closestCenter = jest.fn(); +jest.mock('@dnd-kit/core', () => ({ + ...jest.requireActual('@dnd-kit/core'), + // Since jsdom (used by jest) does not support getBoundingClientRect function + // which is required for drag-n-drop calculations, we mock closestCorners fn + // from dnd-kit to return collided elements as per the test. This allows us to + // test all drag-n-drop handlers. + closestCenter: () => closestCenter(), +})); + describe('', () => { beforeEach(() => { const mocks = initializeMocks(); @@ -187,4 +199,36 @@ describe('', () => { userEvent.click(closeButton); await waitFor(() => expect(screen.queryByTestId('library-sidebar')).not.toBeInTheDocument()); }); + + it('should call update order api on dragging component', async () => { + renderLibraryUnitPage(); + const firstDragHandle = (await screen.findAllByRole('button', { name: 'Drag to reorder' }))[0]; + axiosMock + .onPatch(getLibraryContainerChildrenApiUrl(mockGetContainerMetadata.containerId)) + .reply(200); + closestCenter.mockReturnValue([{ id: 'lb:org1:Demo_course:html:text-1' }]); + await act(async () => { + fireEvent.keyDown(firstDragHandle, { code: 'Space' }); + }); + await act(async () => { + fireEvent.keyDown(firstDragHandle, { code: 'Space' }); + }); + await waitFor(() => expect(mockShowToast).toHaveBeenLastCalledWith('Order updated')); + }); + + it('should show toast error message on update order failure', async () => { + renderLibraryUnitPage(); + const firstDragHandle = (await screen.findAllByRole('button', { name: 'Drag to reorder' }))[0]; + axiosMock + .onPatch(getLibraryContainerChildrenApiUrl(mockGetContainerMetadata.containerId)) + .reply(500); + closestCenter.mockReturnValue([{ id: 'lb:org1:Demo_course:html:text-1' }]); + await act(async () => { + fireEvent.keyDown(firstDragHandle, { code: 'Space' }); + }); + await act(async () => { + fireEvent.keyDown(firstDragHandle, { code: 'Space' }); + }); + await waitFor(() => expect(mockShowToast).toHaveBeenLastCalledWith('Failed to update components order')); + }); }); diff --git a/src/library-authoring/units/LibraryUnitPage.tsx b/src/library-authoring/units/LibraryUnitPage.tsx index fc1eb67161..e362cdb900 100644 --- a/src/library-authoring/units/LibraryUnitPage.tsx +++ b/src/library-authoring/units/LibraryUnitPage.tsx @@ -176,8 +176,8 @@ export const LibraryUnitPage = () => { return ; } + // istanbul ignore if if (isError) { - // istanbul ignore next return ; } diff --git a/src/library-authoring/units/messages.ts b/src/library-authoring/units/messages.ts index e0e288a383..a15eeb8503 100644 --- a/src/library-authoring/units/messages.ts +++ b/src/library-authoring/units/messages.ts @@ -41,6 +41,16 @@ const messages = defineMessages({ defaultMessage: 'Failed to update container.', description: 'Message displayed when container update fails', }, + orderUpdatedMsg: { + id: 'course-authoring.library-authoring.unit-component.order-updated-msg.text', + defaultMessage: 'Order updated', + description: 'Toast message displayed when components are successfully reordered in a unit', + }, + failedOrderUpdatedMsg: { + id: 'course-authoring.library-authoring.unit-component.failed-order-updated-msg.text', + defaultMessage: 'Failed to update components order', + description: 'Toast message displayed when components are successfully reordered in a unit', + }, }); export default messages;