diff --git a/package.json b/package.json index 31c7daf88f..3d91b2cfdc 100644 --- a/package.json +++ b/package.json @@ -120,8 +120,5 @@ }, "peerDependencies": { "decode-uri-component": ">=0.2.2" - }, - "overrides": { - "react-intl": "^6.4.0" } } diff --git a/src/__mocks__/clipboardUnit.js b/src/__mocks__/clipboardUnit.js index d181c94ac6..fb20bde413 100644 --- a/src/__mocks__/clipboardUnit.js +++ b/src/__mocks__/clipboardUnit.js @@ -1,4 +1,4 @@ -module.exports = { +export default { content: { id: 67, userId: 3, diff --git a/src/__mocks__/clipboardXBlock.js b/src/__mocks__/clipboardXBlock.js index 621044e494..ecaf0b50b1 100644 --- a/src/__mocks__/clipboardXBlock.js +++ b/src/__mocks__/clipboardXBlock.js @@ -1,4 +1,4 @@ -module.exports = { +export default { content: { id: 69, userId: 3, diff --git a/src/constants.js b/src/constants.js index f718f549f3..87fb9d9cb8 100644 --- a/src/constants.js +++ b/src/constants.js @@ -66,4 +66,4 @@ export const CLIPBOARD_STATUS = { error: 'error', }; -export const NOT_XBLOCK_TYPES = ['vertical', 'sequential', 'chapter', 'course']; +export const STRUCTURAL_XBLOCK_TYPES = ['vertical', 'sequential', 'chapter', 'course']; diff --git a/src/course-unit/CourseUnit.test.jsx b/src/course-unit/CourseUnit.test.jsx index 31d2312130..4393b50a97 100644 --- a/src/course-unit/CourseUnit.test.jsx +++ b/src/course-unit/CourseUnit.test.jsx @@ -53,7 +53,6 @@ import courseXBlockMessages from './course-xblock/messages'; import addComponentMessages from './add-component/messages'; import { PUBLISH_TYPES, UNIT_VISIBILITY_STATES } from './constants'; import messages from './messages'; -import { copyToClipboard } from '../generic/data/thunks'; import { getContentTaxonomyTagsApiUrl, getContentTaxonomyTagsCountApiUrl } from '../content-tags-drawer/data/api'; let axiosMock; @@ -1174,8 +1173,16 @@ describe('', () => { user_clipboard: clipboardXBlock, }); + axiosMock + .onGet(getCourseUnitApiUrl(courseId)) + .reply(200, { + ...courseUnitIndexMock, + enable_copy_paste_units: true, + }); + + await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch); - await executeThunk(copyToClipboard(blockId), store.dispatch); + expect(getByRole('button', { name: messages.pasteButtonText.defaultMessage })).toBeInTheDocument(); }); @@ -1191,10 +1198,6 @@ describe('', () => { enable_copy_paste_units: true, }); - await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); - - userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); - axiosMock .onGet(getCourseSectionVerticalApiUrl(blockId)) .reply(200, { @@ -1202,9 +1205,10 @@ describe('', () => { user_clipboard: clipboardUnit, }); + await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch); - await executeThunk(copyToClipboard(blockId), store.dispatch); + userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage })); let units = null; @@ -1252,10 +1256,6 @@ describe('', () => { enable_copy_paste_units: true, }); - await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); - - userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); - axiosMock .onGet(getCourseSectionVerticalApiUrl(blockId)) .reply(200, { @@ -1263,9 +1263,10 @@ describe('', () => { user_clipboard: clipboardUnit, }); + await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch); - await executeThunk(copyToClipboard(blockId), store.dispatch); + userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage })); const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock); @@ -1316,10 +1317,6 @@ describe('', () => { enable_copy_paste_units: true, }); - await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); - - userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); - axiosMock .onGet(getCourseSectionVerticalApiUrl(blockId)) .reply(200, { @@ -1327,9 +1324,10 @@ describe('', () => { user_clipboard: clipboardUnit, }); + await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch); - await executeThunk(copyToClipboard(blockId), store.dispatch); + userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage })); const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock); @@ -1380,10 +1378,6 @@ describe('', () => { enable_copy_paste_units: true, }); - await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); - - userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); - axiosMock .onGet(getCourseSectionVerticalApiUrl(blockId)) .reply(200, { @@ -1391,9 +1385,10 @@ describe('', () => { user_clipboard: clipboardUnit, }); + await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch); - await executeThunk(copyToClipboard(blockId), store.dispatch); + userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage })); userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage })); const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock); diff --git a/src/course-unit/__mocks__/clipboardResponse.js b/src/course-unit/__mocks__/clipboardResponse.js index 30a4248c1b..1d8a5a64d6 100644 --- a/src/course-unit/__mocks__/clipboardResponse.js +++ b/src/course-unit/__mocks__/clipboardResponse.js @@ -1,4 +1,4 @@ -module.exports = { +export default { locator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc', courseKey: 'course-v1:edX+L153+3T2023', staticFileNotices: { diff --git a/src/course-unit/clipboard/paste-notification/messages.js b/src/course-unit/clipboard/paste-notification/messages.js index 2786256a87..81f37e4d5f 100644 --- a/src/course-unit/clipboard/paste-notification/messages.js +++ b/src/course-unit/clipboard/paste-notification/messages.js @@ -4,34 +4,48 @@ const messages = defineMessages({ hasConflictingErrorsTitle: { id: 'course-authoring.course-unit.paste-notification.has-conflicting-errors.title', defaultMessage: 'Files need to be updated manually.', + description: 'Title for a notification indicating that files need manual updates ' + + 'due to a conflict in the clipboard.', }, hasConflictingErrorsDescription: { id: 'course-authoring.course-unit.paste-notification.has-conflicting-errors.description', defaultMessage: 'The following files must be updated manually for components to work as intended:', + description: 'Description for the notification indicating which files need manual ' + + 'updates due to a clipboard conflict.', }, hasConflictingErrorsButtonText: { id: 'course-authoring.course-unit.paste-notification.has-conflicting-errors.button.text', defaultMessage: 'Upload files', + description: 'Button text prompting users to upload files to resolve a clipboard conflict.', }, hasErrorsTitle: { id: 'course-authoring.course-unit.paste-notification.has-errors.title', defaultMessage: 'Some errors occurred', + description: 'Title for a notification indicating that some errors occurred, likely ' + + 'related to file conflicts.', }, hasErrorsDescription: { id: 'course-authoring.course-unit.paste-notification.has-errors.description', defaultMessage: 'The following required files could not be added to the course:', + description: 'Description for the notification indicating which required files ' + + 'couldn\'t be added to the course due to errors.', }, hasNewFilesTitle: { id: 'course-authoring.course-unit.paste-notification.has-new-files.title', defaultMessage: 'New file(s) added to Files & Uploads.', + description: 'Title for a notification indicating that new files have been added to ' + + 'the Files & Uploads section.', }, hasNewFilesDescription: { id: 'course-authoring.course-unit.paste-notification.has-new-files.description', defaultMessage: 'The following required files were imported to this course:', + description: 'Description for the notification indicating which required files ' + + 'were imported to the course.', }, hasNewFilesButtonText: { id: 'course-authoring.course-unit.paste-notification.has-new-files.button.text', defaultMessage: 'View files', + description: 'Button text prompting users to view new files imported to the course.', }, }); diff --git a/src/course-unit/constants.js b/src/course-unit/constants.js index 6ca687d01f..b7e7bf5c6b 100644 --- a/src/course-unit/constants.js +++ b/src/course-unit/constants.js @@ -18,22 +18,6 @@ import addComponentMessages from './add-component/messages'; export const UNIT_ICON_TYPES = ['video', 'other', 'vertical', 'problem', 'lock']; -export const NOT_XBLOCK_TYPES = ['vertical', 'sequential', 'chapter', 'course']; - -export const STUDIO_CLIPBOARD_CHANNEL = 'studio_clipboard_channel'; - -/** - * Enum for clipboard status. - * @readonly - * @enum {string} - */ -export const CLIPBOARD_STATUS = { - loading: 'loading', - ready: 'ready', - expired: 'expired', - error: 'error', -}; - export const COMPONENT_TYPES = { advanced: 'advanced', discussion: 'discussion', diff --git a/src/course-unit/data/slice.js b/src/course-unit/data/slice.js index 87b60094a4..02edc09757 100644 --- a/src/course-unit/data/slice.js +++ b/src/course-unit/data/slice.js @@ -17,7 +17,7 @@ const slice = createSlice({ }, unit: {}, courseSectionVertical: {}, - courseVerticalChildren: {}, + courseVerticalChildren: { children: [], isPublished: true }, staticFileNotices: {}, }, reducers: { diff --git a/src/generic/clipboard/hooks/useCopyToClipboard.js b/src/generic/clipboard/hooks/useCopyToClipboard.js index 862788e0bb..86303fab95 100644 --- a/src/generic/clipboard/hooks/useCopyToClipboard.js +++ b/src/generic/clipboard/hooks/useCopyToClipboard.js @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; -import { CLIPBOARD_STATUS, NOT_XBLOCK_TYPES, STUDIO_CLIPBOARD_CHANNEL } from '../../../constants'; +import { CLIPBOARD_STATUS, STRUCTURAL_XBLOCK_TYPES, STUDIO_CLIPBOARD_CHANNEL } from '../../../constants'; import { getClipboardData } from '../../data/selectors'; /** @@ -23,7 +23,7 @@ const useCopyToClipboard = (canEdit = true) => { // Function to refresh the paste button's visibility const refreshPasteButton = (data) => { const isPasteable = canEdit && data?.content && data.content.status !== CLIPBOARD_STATUS.expired; - const isPasteableXBlock = isPasteable && !NOT_XBLOCK_TYPES.includes(data.content.blockType); + const isPasteableXBlock = isPasteable && !STRUCTURAL_XBLOCK_TYPES.includes(data.content.blockType); const isPasteableUnit = isPasteable && data.content.blockType === 'vertical'; setShowPasteXBlock(!!isPasteableXBlock); diff --git a/src/generic/clipboard/paste-component/components/WhatsInClipboard.jsx b/src/generic/clipboard/paste-component/components/WhatsInClipboard.jsx index d4e532b13c..aca6d3f0cc 100644 --- a/src/generic/clipboard/paste-component/components/WhatsInClipboard.jsx +++ b/src/generic/clipboard/paste-component/components/WhatsInClipboard.jsx @@ -14,7 +14,7 @@ const WhatsInClipboard = ({ const handleKeyDown = ({ key }) => { if (key === 'Tab') { - popoverElementRef.current.focus(); + popoverElementRef.current?.focus(); handlePopoverToggle(true); } }; diff --git a/src/generic/clipboard/paste-component/constants.js b/src/generic/clipboard/paste-component/constants.js index 454f332c84..1dc7a1c526 100644 --- a/src/generic/clipboard/paste-component/constants.js +++ b/src/generic/clipboard/paste-component/constants.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; +/* eslint-disable import/prefer-default-export */ export const clipboardPropsTypes = { sourceEditUrl: PropTypes.string.isRequired, content: PropTypes.shape({ @@ -8,5 +9,3 @@ export const clipboardPropsTypes = { }).isRequired, sourceContextTitle: PropTypes.string.isRequired, }; - -export const OVERLAY_TRIGGERS = ['hover', 'focus']; diff --git a/src/generic/clipboard/paste-component/index.jsx b/src/generic/clipboard/paste-component/index.jsx index af4674952a..a6602bb079 100644 --- a/src/generic/clipboard/paste-component/index.jsx +++ b/src/generic/clipboard/paste-component/index.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { OverlayTrigger, Popover } from '@openedx/paragon'; import { PopoverContent, PasteButton, WhatsInClipboard } from './components'; -import { clipboardPropsTypes, OVERLAY_TRIGGERS } from './constants'; +import { clipboardPropsTypes } from './constants'; const PasteComponent = ({ onClick, clipboardData, text, className, @@ -36,7 +36,6 @@ const PasteComponent = ({