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 = ({