Repository: openedx/frontend-app-authoring
Pull request #1887: fix: do open editor of new xblock when duplicating
Author: login: DanielVZ96
Labels: name: open-source-contribution
description: PR author is not from Axim or 2U
Created: 2025-05-01T02:30:37Z | Merged: 2025-05-20T22:51:43Z

## Description

Fixes bug where after duplicating an xblock, the editor modal of the old xblock is being open instead of the new copied xblock.

## Supporting information

- [Comment that reported this issue](https://github.com/openedx/frontend-app-authoring/pull/1838#issuecomment-2831139050)
- Reported again here: https://github.com/openedx/frontend-app-authoring/issues/1919

## Testing instructions

* [Use this branch of edx-platform](https://github.com/openedx/edx-platform/pull/36641)
* On an unit page duplicate an xblock
* Assert that:
1. Only the MFE modal is open
2. The MFE modal is pointing to the new xblock.


## Other information

[Private-ref](https://tasks.opencraft.com/browse/FAL-4153)


Diff:
diff --git a/src/course-unit/CourseUnit.test.jsx b/src/course-unit/CourseUnit.test.jsx
index 06d87e6cfe..7af3e9977d 100644
--- a/src/course-unit/CourseUnit.test.jsx
+++ b/src/course-unit/CourseUnit.test.jsx
@@ -65,6 +65,7 @@ import xblockContainerIframeMessages from './xblock-container-iframe/messages';
 import headerNavigationsMessages from './header-navigations/messages';
 import sidebarMessages from './sidebar/messages';
 import messages from './messages';
+import * as selectors from '../data/selectors';
 
 let axiosMock;
 let store;
@@ -166,27 +167,27 @@ describe('<CourseUnit />', () => {
   });
 
   it('render CourseUnit component correctly', async () => {
-    const { getByText, getByRole, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
     const currentSectionName = courseUnitIndexMock.ancestor_info.ancestors[1].display_name;
     const currentSubSectionName = courseUnitIndexMock.ancestor_info.ancestors[1].display_name;
 
     await waitFor(() => {
-      const unitHeaderTitle = getByTestId('unit-header-title');
-      expect(getByText(unitDisplayName)).toBeInTheDocument();
+      const unitHeaderTitle = screen.getByTestId('unit-header-title');
+      expect(screen.getByText(unitDisplayName)).toBeInTheDocument();
       expect(within(unitHeaderTitle).getByRole('button', { name: headerTitleMessages.altButtonEdit.defaultMessage })).toBeInTheDocument();
       expect(within(unitHeaderTitle).getByRole('button', { name: headerTitleMessages.altButtonSettings.defaultMessage })).toBeInTheDocument();
-      expect(getByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage })).toBeInTheDocument();
-      expect(getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage })).toBeInTheDocument();
-      expect(getByRole('button', { name: currentSectionName })).toBeInTheDocument();
-      expect(getByRole('button', { name: currentSubSectionName })).toBeInTheDocument();
+      expect(screen.getByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage })).toBeInTheDocument();
+      expect(screen.getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage })).toBeInTheDocument();
+      expect(screen.getByRole('button', { name: currentSectionName })).toBeInTheDocument();
+      expect(screen.getByRole('button', { name: currentSubSectionName })).toBeInTheDocument();
     });
   });
 
   it('renders the course unit iframe with correct attributes', async () => {
-    const { getByTitle } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(iframe).toHaveAttribute('src', `${getConfig().STUDIO_BASE_URL}/container_embed/${blockId}`);
       expect(iframe).toHaveAttribute('allow', IFRAME_FEATURE_POLICY);
       expect(iframe).toHaveAttribute('style', 'height: 0px;');
@@ -210,27 +211,27 @@ describe('<CourseUnit />', () => {
   });
 
   it('displays an error alert when a studioAjaxError message is received', async () => {
-    const { getByTitle, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      const xblocksIframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const xblocksIframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(xblocksIframe).toBeInTheDocument();
       simulatePostMessageEvent(messageTypes.studioAjaxError, {
         error: 'Some error text...',
       });
     });
-    expect(getByTestId('saving-error-alert')).toBeInTheDocument();
+    expect(screen.getByTestId('saving-error-alert')).toBeInTheDocument();
   });
 
   it('renders XBlock iframe and opens legacy edit modal on editXBlock message', async () => {
-    const { getByTitle } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      const xblocksIframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const xblocksIframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(xblocksIframe).toBeInTheDocument();
       simulatePostMessageEvent(messageTypes.editXBlock, { id: blockId });
 
-      const legacyXBlockEditModalIframe = getByTitle(
+      const legacyXBlockEditModalIframe = screen.getByTitle(
         xblockContainerIframeMessages.legacyEditModalIframeTitle.defaultMessage,
       );
       expect(legacyXBlockEditModalIframe).toBeInTheDocument();
@@ -248,14 +249,14 @@ describe('<CourseUnit />', () => {
   });
 
   it('closes the legacy edit modal when closeXBlockEditorModal message is received', async () => {
-    const { getByTitle, queryByTitle } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      const xblocksIframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const xblocksIframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(xblocksIframe).toBeInTheDocument();
       simulatePostMessageEvent(messageTypes.closeXBlockEditorModal, { id: blockId });
 
-      const legacyXBlockEditModalIframe = queryByTitle(
+      const legacyXBlockEditModalIframe = screen.queryByTitle(
         xblockContainerIframeMessages.legacyEditModalIframeTitle.defaultMessage,
       );
       expect(legacyXBlockEditModalIframe).not.toBeInTheDocument();
@@ -263,14 +264,14 @@ describe('<CourseUnit />', () => {
   });
 
   it('closes legacy edit modal and updates course unit sidebar after saveEditedXBlockData message', async () => {
-    const { getByTitle, queryByTitle, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      const xblocksIframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const xblocksIframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(xblocksIframe).toBeInTheDocument();
       simulatePostMessageEvent(messageTypes.saveEditedXBlockData);
 
-      const legacyXBlockEditModalIframe = queryByTitle(
+      const legacyXBlockEditModalIframe = screen.queryByTitle(
         xblockContainerIframeMessages.legacyEditModalIframeTitle.defaultMessage,
       );
       expect(legacyXBlockEditModalIframe).not.toBeInTheDocument();
@@ -285,7 +286,7 @@ describe('<CourseUnit />', () => {
       });
 
     await waitFor(() => {
-      const courseUnitSidebar = getByTestId('course-unit-sidebar');
+      const courseUnitSidebar = screen.getByTestId('course-unit-sidebar');
       expect(
         within(courseUnitSidebar).getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage),
       ).toBeInTheDocument();
@@ -304,10 +305,10 @@ describe('<CourseUnit />', () => {
   });
 
   it('updates course unit sidebar after receiving refreshPositions message', async () => {
-    const { getByTitle, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      const xblocksIframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const xblocksIframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(xblocksIframe).toBeInTheDocument();
       simulatePostMessageEvent(messageTypes.refreshPositions);
     });
@@ -321,7 +322,7 @@ describe('<CourseUnit />', () => {
       });
 
     await waitFor(() => {
-      const courseUnitSidebar = getByTestId('course-unit-sidebar');
+      const courseUnitSidebar = screen.getByTestId('course-unit-sidebar');
       expect(
         within(courseUnitSidebar).getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage),
       ).toBeInTheDocument();
@@ -340,12 +341,10 @@ describe('<CourseUnit />', () => {
   });
 
   it('checks whether xblock is removed when the corresponding delete button is clicked and the sidebar is the updated', async () => {
-    const {
-      getByTitle, getByText, queryByRole, getByRole,
-    } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(async () => {
-      const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(iframe).toHaveAttribute(
         'aria-label',
         xblockContainerIframeMessages.xblockIframeLabel.defaultMessage
@@ -356,10 +355,10 @@ describe('<CourseUnit />', () => {
         usageId: courseVerticalChildrenMock.children[0].block_id,
       });
 
-      expect(getByText(/Delete this component?/i)).toBeInTheDocument();
-      expect(getByText(/Deleting this component is permanent and cannot be undone./i)).toBeInTheDocument();
+      expect(screen.getByText(/Delete this component?/i)).toBeInTheDocument();
+      expect(screen.getByText(/Deleting this component is permanent and cannot be undone./i)).toBeInTheDocument();
 
-      const dialog = getByRole('dialog');
+      const dialog = screen.getByRole('dialog');
       expect(dialog).toBeInTheDocument();
 
       // Find the Cancel and Delete buttons within the iframe by their specific classes
@@ -372,7 +371,7 @@ describe('<CourseUnit />', () => {
         usageId: courseVerticalChildrenMock.children[0].block_id,
       });
 
-      expect(getByRole('dialog')).toBeInTheDocument();
+      expect(screen.getByRole('dialog')).toBeInTheDocument();
       userEvent.click(deleteButton);
     });
 
@@ -393,14 +392,14 @@ describe('<CourseUnit />', () => {
 
     await waitFor(() => {
       // check if the sidebar status is Published and Live
-      expect(getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(
         sidebarMessages.publishLastPublished.defaultMessage
           .replace('{publishedOn}', courseUnitIndexMock.published_on)
           .replace('{publishedBy}', userName),
       )).toBeInTheDocument();
-      expect(queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
-      expect(getByText(unitDisplayName)).toBeInTheDocument();
+      expect(screen.queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
+      expect(screen.getByText(unitDisplayName)).toBeInTheDocument();
     });
 
     axiosMock
@@ -431,28 +430,28 @@ describe('<CourseUnit />', () => {
     await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
 
     await waitFor(() => {
-      const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(iframe).toHaveAttribute(
         'aria-label',
         xblockContainerIframeMessages.xblockIframeLabel.defaultMessage
           .replace('{xblockCount}', updatedCourseVerticalChildren.length),
       );
       // after removing the xblock, the sidebar status changes to Draft (unpublished changes)
-      expect(getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
+      expect(screen.getByText(
         sidebarMessages.publishInfoDraftSaved.defaultMessage
           .replace('{editedOn}', courseUnitIndexMock.edited_on)
           .replace('{editedBy}', courseUnitIndexMock.edited_by),
       )).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(
         sidebarMessages.releaseInfoWithSection.defaultMessage
           .replace('{sectionName}', courseUnitIndexMock.release_date_from),
       )).toBeInTheDocument();
@@ -460,9 +459,7 @@ describe('<CourseUnit />', () => {
   });
 
   it('checks if xblock is a duplicate when the corresponding duplicate button is clicked and if the sidebar status is updated', async () => {
-    const {
-      getByTitle, getByRole, getByText, queryByRole,
-    } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     simulatePostMessageEvent(messageTypes.duplicateXBlock, {
       id: courseVerticalChildrenMock.children[0].block_id,
@@ -478,8 +475,14 @@ describe('<CourseUnit />', () => {
     const updatedCourseVerticalChildren = [
       ...courseVerticalChildrenMock.children,
       {
-        ...courseVerticalChildrenMock.children[0],
         name: 'New Cloned XBlock',
+        block_id: '1234567890',
+        block_type: 'drag-and-drop-v2',
+        user_partition_info: {
+          selectable_partitions: [],
+          selected_partition_index: -1,
+          selected_groups_label: '',
+        },
       },
     ];
 
@@ -491,9 +494,9 @@ describe('<CourseUnit />', () => {
       });
 
     await waitFor(() => {
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
 
-      const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(iframe).toHaveAttribute(
         'aria-label',
         xblockContainerIframeMessages.xblockIframeLabel.defaultMessage
@@ -522,14 +525,14 @@ describe('<CourseUnit />', () => {
 
     await waitFor(() => {
       // check if the sidebar status is Published and Live
-      expect(getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(
         sidebarMessages.publishLastPublished.defaultMessage
           .replace('{publishedOn}', courseUnitIndexMock.published_on)
           .replace('{publishedBy}', userName),
       )).toBeInTheDocument();
-      expect(queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
-      expect(getByText(unitDisplayName)).toBeInTheDocument();
+      expect(screen.queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
+      expect(screen.getByText(unitDisplayName)).toBeInTheDocument();
     });
 
     axiosMock
@@ -538,7 +541,7 @@ describe('<CourseUnit />', () => {
     await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
 
     await waitFor(() => {
-      const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(iframe).toHaveAttribute(
         'aria-label',
         xblockContainerIframeMessages.xblockIframeLabel.defaultMessage
@@ -546,21 +549,21 @@ describe('<CourseUnit />', () => {
       );
 
       // after duplicate the xblock, the sidebar status changes to Draft (unpublished changes)
-      expect(getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
+      expect(screen.getByText(
         sidebarMessages.publishInfoDraftSaved.defaultMessage
           .replace('{editedOn}', courseUnitIndexMock.edited_on)
           .replace('{editedBy}', courseUnitIndexMock.edited_by),
       )).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(
         sidebarMessages.releaseInfoWithSection.defaultMessage
           .replace('{sectionName}', courseUnitIndexMock.release_date_from),
       )).toBeInTheDocument();
@@ -570,19 +573,19 @@ describe('<CourseUnit />', () => {
   it('handles CourseUnit header action buttons', async () => {
     const { open } = window;
     window.open = jest.fn();
-    const { getByRole } = render(<RootWrapper />);
+    render(<RootWrapper />);
     const {
       draft_preview_link: draftPreviewLink,
       published_preview_link: publishedPreviewLink,
     } = courseSectionVerticalMock;
 
     await waitFor(() => {
-      const viewLiveButton = getByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage });
+      const viewLiveButton = screen.getByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage });
       userEvent.click(viewLiveButton);
       expect(window.open).toHaveBeenCalled();
       expect(window.open).toHaveBeenCalledWith(publishedPreviewLink, '_blank');
 
-      const previewButton = getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage });
+      const previewButton = screen.getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage });
       userEvent.click(previewButton);
       expect(window.open).toHaveBeenCalled();
       expect(window.open).toHaveBeenCalledWith(draftPreviewLink, '_blank');
@@ -592,12 +595,7 @@ describe('<CourseUnit />', () => {
   });
 
   it('checks courseUnit title changing when edit query is successfully', async () => {
-    const {
-      findByText,
-      queryByRole,
-      getByRole,
-      getByTestId,
-    } = render(<RootWrapper />);
+    render(<RootWrapper />);
     let editTitleButton = null;
     let titleEditField = null;
     const newDisplayName = `${unitDisplayName} new`;
@@ -633,7 +631,7 @@ describe('<CourseUnit />', () => {
       });
 
     await waitFor(() => {
-      const unitHeaderTitle = getByTestId('unit-header-title');
+      const unitHeaderTitle = screen.getByTestId('unit-header-title');
       editTitleButton = within(unitHeaderTitle)
         .getByRole('button', { name: headerTitleMessages.altButtonEdit.defaultMessage });
       titleEditField = within(unitHeaderTitle)
@@ -641,7 +639,7 @@ describe('<CourseUnit />', () => {
     });
     expect(titleEditField).not.toBeInTheDocument();
     userEvent.click(editTitleButton);
-    titleEditField = getByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
+    titleEditField = screen.getByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
 
     await userEvent.clear(titleEditField);
     await userEvent.type(titleEditField, newDisplayName);
@@ -649,9 +647,10 @@ describe('<CourseUnit />', () => {
 
     expect(titleEditField).toHaveValue(newDisplayName);
 
-    titleEditField = queryByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
+    titleEditField = screen.queryByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
+    titleEditField = screen.queryByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
     expect(titleEditField).not.toBeInTheDocument();
-    expect(await findByText(newDisplayName)).toBeInTheDocument();
+    expect(await screen.findByText(newDisplayName)).toBeInTheDocument();
   });
 
   it('doesn\'t handle creating xblock and displays an error message', async () => {
@@ -675,10 +674,10 @@ describe('<CourseUnit />', () => {
     axiosMock
       .onPost(postXBlockBaseApiUrl({ type: 'problem', category: 'problem', parentLocator: blockId }))
       .reply(200, courseCreateXblockMock);
-    const { getByText, getByRole } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
     });
 
     axiosMock
@@ -698,7 +697,7 @@ describe('<CourseUnit />', () => {
     await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
 
     await waitFor(() => {
-      const problemButton = getByRole('button', {
+      const problemButton = screen.getByRole('button', {
         name: new RegExp(`problem ${addComponentMessages.buttonText.defaultMessage} Problem`, 'i'),
         hidden: true,
       });
@@ -707,7 +706,7 @@ describe('<CourseUnit />', () => {
     });
 
     await waitFor(() => {
-      expect(getByRole('heading', {
+      expect(screen.getByRole('heading', {
         name: new RegExp(`${addComponentMessages.blockEditorModalTitle.defaultMessage}`, 'i'),
       })).toBeInTheDocument();
     });
@@ -719,28 +718,28 @@ describe('<CourseUnit />', () => {
     await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
 
     // after creating problem xblock, the sidebar status changes to Draft (unpublished changes)
-    expect(getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
-    expect(getByText(
+    expect(screen.getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
+    expect(screen.getByText(
       sidebarMessages.publishInfoDraftSaved.defaultMessage
         .replace('{editedOn}', courseUnitIndexMock.edited_on)
         .replace('{editedBy}', courseUnitIndexMock.edited_by),
     )).toBeInTheDocument();
-    expect(getByText(
+    expect(screen.getByText(
       sidebarMessages.releaseInfoWithSection.defaultMessage
         .replace('{sectionName}', courseUnitIndexMock.release_date_from),
     )).toBeInTheDocument();
   });
 
   it('correct addition of a new course unit after click on the "Add new unit" button', async () => {
-    const { getByRole, getAllByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
     let units = null;
     const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock);
     const updatedAncestorsChild = updatedCourseSectionVerticalData.xblock_info.ancestor_info.ancestors[0];
@@ -750,7 +749,7 @@ describe('<CourseUnit />', () => {
     ]);
 
     await waitFor(async () => {
-      units = getAllByTestId('course-unit-btn');
+      units = screen.getAllByTestId('course-unit-btn');
       const courseUnits = courseSectionVerticalMock.xblock_info.ancestor_info.ancestors[0].child_info.children;
       expect(units).toHaveLength(courseUnits.length);
     });
@@ -767,8 +766,8 @@ describe('<CourseUnit />', () => {
 
     await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-    const addNewUnitBtn = getByRole('button', { name: courseSequenceMessages.newUnitBtnText.defaultMessage });
-    units = getAllByTestId('course-unit-btn');
+    const addNewUnitBtn = screen.getByRole('button', { name: courseSequenceMessages.newUnitBtnText.defaultMessage });
+    units = screen.getAllByTestId('course-unit-btn');
     const updatedCourseUnits = updatedCourseSectionVerticalData
       .xblock_info.ancestor_info.ancestors[0].child_info.children;
 
@@ -780,7 +779,7 @@ describe('<CourseUnit />', () => {
   });
 
   it('the sequence unit is updated after changing the unit header', async () => {
-    const { getAllByTestId, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
     const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock);
     const updatedAncestorsChild = updatedCourseSectionVerticalData.xblock_info.ancestor_info.ancestors[0];
     set(updatedCourseSectionVerticalData, 'xblock_info.ancestor_info.ancestors[0].child_info.children', [
@@ -812,7 +811,7 @@ describe('<CourseUnit />', () => {
 
     await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-    const unitHeaderTitle = getByTestId('unit-header-title');
+    const unitHeaderTitle = screen.getByTestId('unit-header-title');
 
     const editTitleButton = within(unitHeaderTitle).getByRole('button', { name: headerTitleMessages.altButtonEdit.defaultMessage });
     userEvent.click(editTitleButton);
@@ -824,19 +823,95 @@ describe('<CourseUnit />', () => {
     await userEvent.tab();
 
     await waitFor(async () => {
-      const units = getAllByTestId('course-unit-btn');
+      const units = screen.getAllByTestId('course-unit-btn');
       expect(units.some(unit => unit.title === newDisplayName)).toBe(true);
     });
   });
 
+  it('handles creating Video xblock and showing editor modal using videogalleryflow', async () => {
+    const waffleSpy = jest.spyOn(selectors, 'getWaffleFlags').mockReturnValue({ useVideoGalleryFlow: true });
+
+    axiosMock
+      .onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
+      .reply(200, courseCreateXblockMock);
+    render(<RootWrapper />);
+
+    await waitFor(() => {
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
+    });
+
+    axiosMock
+      .onPost(getXBlockBaseApiUrl(blockId), {
+        publish: PUBLISH_TYPES.makePublic,
+      })
+      .reply(200, { dummy: 'value' });
+    axiosMock
+      .onGet(getCourseUnitApiUrl(blockId))
+      .reply(200, {
+        ...courseUnitIndexMock,
+        visibility_state: UNIT_VISIBILITY_STATES.live,
+        has_changes: false,
+        published_by: userName,
+      });
+
+    await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
+
+    await waitFor(() => {
+      // check if the sidebar status is Published and Live
+      expect(screen.getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
+    });
+
+    expect(screen.getByText(
+      sidebarMessages.publishLastPublished.defaultMessage
+        .replace('{publishedOn}', courseUnitIndexMock.published_on)
+        .replace('{publishedBy}', userName),
+    )).toBeInTheDocument();
+    expect(screen.queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
+
+    const videoButton = screen.getByRole('button', {
+      name: new RegExp(`${addComponentMessages.buttonText.defaultMessage} Video`, 'i'),
+      hidden: true,
+    });
+
+    userEvent.click(videoButton);
+
+    axiosMock
+      .onGet(getCourseUnitApiUrl(blockId))
+      .reply(200, courseUnitIndexMock);
+
+    await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
+
+    // after creating video xblock, the sidebar status changes to Draft (unpublished changes)
+    expect(screen.getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
+    expect(screen.getByText(
+      sidebarMessages.publishInfoDraftSaved.defaultMessage
+        .replace('{editedOn}', courseUnitIndexMock.edited_on)
+        .replace('{editedBy}', courseUnitIndexMock.edited_by),
+    )).toBeInTheDocument();
+    expect(screen.getByText(
+      sidebarMessages.releaseInfoWithSection.defaultMessage
+        .replace('{sectionName}', courseUnitIndexMock.release_date_from),
+    )).toBeInTheDocument();
+    expect(screen.getByRole('heading', { name: /add video to your course/i, hidden: true })).toBeInTheDocument();
+    waffleSpy.mockRestore();
+  });
+
   it('handles creating Video xblock and showing editor modal', async () => {
     axiosMock
       .onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
       .reply(200, courseCreateXblockMock);
-    const { getByText, queryByRole, getByRole } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
     });
 
     axiosMock
@@ -857,15 +932,15 @@ describe('<CourseUnit />', () => {
 
     await waitFor(() => {
       // check if the sidebar status is Published and Live
-      expect(getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(
         sidebarMessages.publishLastPublished.defaultMessage
           .replace('{publishedOn}', courseUnitIndexMock.published_on)
           .replace('{publishedBy}', userName),
       )).toBeInTheDocument();
-      expect(queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
+      expect(screen.queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
 
-      const videoButton = getByRole('button', {
+      const videoButton = screen.getByRole('button', {
         name: new RegExp(`${addComponentMessages.buttonText.defaultMessage} Video`, 'i'),
         hidden: true,
       });
@@ -886,45 +961,45 @@ describe('<CourseUnit />', () => {
     await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
 
     // after creating video xblock, the sidebar status changes to Draft (unpublished changes)
-    expect(getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
-    expect(getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
-    expect(getByText(
+    expect(screen.getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
+    expect(screen.getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
+    expect(screen.getByText(
       sidebarMessages.publishInfoDraftSaved.defaultMessage
         .replace('{editedOn}', courseUnitIndexMock.edited_on)
         .replace('{editedBy}', courseUnitIndexMock.edited_by),
     )).toBeInTheDocument();
-    expect(getByText(
+    expect(screen.getByText(
       sidebarMessages.releaseInfoWithSection.defaultMessage
         .replace('{sectionName}', courseUnitIndexMock.release_date_from),
     )).toBeInTheDocument();
   });
 
   it('renders course unit details for a draft with unpublished changes', async () => {
-    const { getByText } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      expect(getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityStaffAndLearnersTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.releaseStatusTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.sidebarBodyNote.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityWillBeVisibleToTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.visibilityCheckboxTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.actionButtonPublishTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.actionButtonDiscardChangesTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(courseUnitIndexMock.release_date)).toBeInTheDocument();
+      expect(screen.getByText(
         sidebarMessages.publishInfoDraftSaved.defaultMessage
           .replace('{editedOn}', courseUnitIndexMock.edited_on)
           .replace('{editedBy}', courseUnitIndexMock.edited_by),
       )).toBeInTheDocument();
-      expect(getByText(
+      expect(screen.getByText(
         sidebarMessages.releaseInfoWithSection.defaultMessage
           .replace('{sectionName}', courseUnitIndexMock.release_date_from),
       )).toBeInTheDocument();
@@ -932,14 +1007,14 @@ describe('<CourseUnit />', () => {
   });
 
   it('renders course unit details in the sidebar', async () => {
-    const { getByText } = render(<RootWrapper />);
+    render(<RootWrapper />);
     const courseUnitLocationId = extractCourseUnitId(courseUnitIndexMock.id);
 
     await waitFor(() => {
-      expect(getByText(sidebarMessages.sidebarHeaderUnitLocationTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.unitLocationTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(courseUnitLocationId)).toBeInTheDocument();
-      expect(getByText(sidebarMessages.unitLocationDescription.defaultMessage
+      expect(screen.getByText(sidebarMessages.sidebarHeaderUnitLocationTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.unitLocationTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(courseUnitLocationId)).toBeInTheDocument();
+      expect(screen.getByText(sidebarMessages.unitLocationDescription.defaultMessage
         .replace('{id}', courseUnitLocationId))).toBeInTheDocument();
     });
   });
@@ -977,13 +1052,13 @@ describe('<CourseUnit />', () => {
   });
 
   it('should toggle visibility from sidebar and update course unit state accordingly', async () => {
-    const { getByRole, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
     let courseUnitSidebar;
     let draftUnpublishedChangesHeading;
     let visibilityCheckbox;
 
     await waitFor(() => {
-      courseUnitSidebar = getByTestId('course-unit-sidebar');
+      courseUnitSidebar = screen.getByTestId('course-unit-sidebar');
 
       draftUnpublishedChangesHeading = within(courseUnitSidebar)
         .getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage);
@@ -1020,7 +1095,7 @@ describe('<CourseUnit />', () => {
 
     userEvent.click(visibilityCheckbox);
 
-    const modalNotification = getByRole('dialog');
+    const modalNotification = screen.getByRole('dialog');
     const makeVisibilityBtn = within(modalNotification).getByRole('button', { name: sidebarMessages.modalMakeVisibilityActionButtonText.defaultMessage });
     const cancelBtn = within(modalNotification).getByRole('button', { name: sidebarMessages.modalMakeVisibilityCancelButtonText.defaultMessage });
     const headingElement = within(modalNotification).getByRole('heading', { name: sidebarMessages.modalMakeVisibilityTitle.defaultMessage, class: 'pgn__modal-title' });
@@ -1052,12 +1127,12 @@ describe('<CourseUnit />', () => {
   });
 
   it('should publish course unit after click on the "Publish" button', async () => {
-    const { getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
     let courseUnitSidebar;
     let publishBtn;
 
     await waitFor(() => {
-      courseUnitSidebar = getByTestId('course-unit-sidebar');
+      courseUnitSidebar = screen.getByTestId('course-unit-sidebar');
       publishBtn = within(courseUnitSidebar).queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage });
       expect(publishBtn).toBeInTheDocument();
 
@@ -1091,12 +1166,12 @@ describe('<CourseUnit />', () => {
   });
 
   it('should discard changes after click on the "Discard changes" button', async () => {
-    const { getByTestId, getByRole } = render(<RootWrapper />);
+    render(<RootWrapper />);
     let courseUnitSidebar;
     let discardChangesBtn;
 
     await waitFor(() => {
-      courseUnitSidebar = getByTestId('course-unit-sidebar');
+      courseUnitSidebar = screen.getByTestId('course-unit-sidebar');
 
       const draftUnpublishedChangesHeading = within(courseUnitSidebar)
         .getByText(sidebarMessages.sidebarTitleDraftUnpublishedChanges.defaultMessage);
@@ -1106,7 +1181,7 @@ describe('<CourseUnit />', () => {
 
       userEvent.click(discardChangesBtn);
 
-      const modalNotification = getByRole('dialog');
+      const modalNotification = screen.getByRole('dialog');
       expect(modalNotification).toBeInTheDocument();
       expect(within(modalNotification)
         .getByText(sidebarMessages.modalDiscardUnitChangesDescription.defaultMessage)).toBeInTheDocument();
@@ -1143,7 +1218,7 @@ describe('<CourseUnit />', () => {
   });
 
   it('should toggle visibility from header configure modal and update course unit state accordingly', async () => {
-    const { getByRole, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
     let courseUnitSidebar;
     let sidebarVisibilityCheckbox;
     let modalVisibilityCheckbox;
@@ -1151,16 +1226,16 @@ describe('<CourseUnit />', () => {
     let restrictAccessSelect;
 
     await waitFor(() => {
-      courseUnitSidebar = getByTestId('course-unit-sidebar');
+      courseUnitSidebar = screen.getByTestId('course-unit-sidebar');
       sidebarVisibilityCheckbox = within(courseUnitSidebar)
         .getByLabelText(sidebarMessages.visibilityCheckboxTitle.defaultMessage);
       expect(sidebarVisibilityCheckbox).not.toBeChecked();
 
-      const headerConfigureBtn = getByRole('button', { name: /settings/i });
+      const headerConfigureBtn = screen.getByRole('button', { name: /settings/i });
       expect(headerConfigureBtn).toBeInTheDocument();
 
       userEvent.click(headerConfigureBtn);
-      configureModal = getByTestId('configure-modal');
+      configureModal = screen.getByTestId('configure-modal');
       restrictAccessSelect = within(configureModal)
         .getByRole('combobox', { name: configureModalMessages.restrictAccessTo.defaultMessage });
       expect(within(configureModal)
@@ -1216,8 +1291,8 @@ describe('<CourseUnit />', () => {
       ...getConfig(),
       ENABLE_TAGGING_TAXONOMY_PAGES: 'true',
     });
-    const { getByText } = render(<RootWrapper />);
-    await waitFor(() => { expect(getByText('Unit tags')).toBeInTheDocument(); });
+    render(<RootWrapper />);
+    await waitFor(() => { expect(screen.getByText('Unit tags')).toBeInTheDocument(); });
   });
 
   it('hides the Tags sidebar when not enabled', async () => {
@@ -1225,15 +1300,13 @@ describe('<CourseUnit />', () => {
       ...getConfig(),
       ENABLE_TAGGING_TAXONOMY_PAGES: 'false',
     });
-    const { queryByText } = render(<RootWrapper />);
-    await waitFor(() => { expect(queryByText('Unit tags')).not.toBeInTheDocument(); });
+    render(<RootWrapper />);
+    await waitFor(() => { expect(screen.queryByText('Unit tags')).not.toBeInTheDocument(); });
   });
 
   describe('Copy paste functionality', () => {
     it('should copy a unit, paste it as a new unit, and update the course section vertical data', async () => {
-      const {
-        getAllByTestId, getByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onGet(getCourseUnitApiUrl(courseId))
@@ -1245,8 +1318,8 @@ describe('<CourseUnit />', () => {
       await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
-      userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
 
       let units = null;
       const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock);
@@ -1257,7 +1330,7 @@ describe('<CourseUnit />', () => {
       ]);
 
       await waitFor(() => {
-        units = getAllByTestId('course-unit-btn');
+        units = screen.getAllByTestId('course-unit-btn');
         const courseUnits = courseSectionVerticalMock.xblock_info.ancestor_info.ancestors[0].child_info.children;
         expect(units).toHaveLength(courseUnits.length);
       });
@@ -1273,7 +1346,7 @@ describe('<CourseUnit />', () => {
 
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-      units = getAllByTestId('course-unit-btn');
+      units = screen.getAllByTestId('course-unit-btn');
       const updatedCourseUnits = updatedCourseSectionVerticalData
         .xblock_info.ancestor_info.ancestors[0].child_info.children;
 
@@ -1284,7 +1357,7 @@ describe('<CourseUnit />', () => {
     });
 
     it('should increase the number of course XBlocks after copying and pasting a block', async () => {
-      const { getByRole, getByTitle } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       simulatePostMessageEvent(messageTypes.copyXBlock, {
         id: courseVerticalChildrenMock.children[0].block_id,
@@ -1303,11 +1376,11 @@ describe('<CourseUnit />', () => {
       await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
-      userEvent.click(getByRole('button', { name: messages.pasteButtonText.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: messages.pasteButtonText.defaultMessage }));
 
       await waitFor(() => {
-        const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+        const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
         expect(iframe).toHaveAttribute(
           'aria-label',
           xblockContainerIframeMessages.xblockIframeLabel.defaultMessage
@@ -1343,7 +1416,7 @@ describe('<CourseUnit />', () => {
       await executeThunk(fetchCourseVerticalChildrenData(blockId), store.dispatch);
 
       await waitFor(() => {
-        const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+        const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
         expect(iframe).toHaveAttribute(
           'aria-label',
           xblockContainerIframeMessages.xblockIframeLabel.defaultMessage
@@ -1353,9 +1426,7 @@ describe('<CourseUnit />', () => {
     });
 
     it('displays a notification about new files after pasting a component', async () => {
-      const {
-        queryByTestId, getByTestId, getByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onGet(getCourseUnitApiUrl(courseId))
@@ -1367,8 +1438,8 @@ describe('<CourseUnit />', () => {
       await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
-      userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
 
       const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock);
       const updatedAncestorsChild = updatedCourseSectionVerticalData.xblock_info.ancestor_info.ancestors[0];
@@ -1387,7 +1458,7 @@ describe('<CourseUnit />', () => {
       global.localStorage.setItem('staticFileNotices', JSON.stringify(clipboardMockResponse.staticFileNotices));
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
       await executeThunk(createNewCourseXBlock(camelCaseObject(postXBlockBody), null, blockId), store.dispatch);
-      const newFilesAlert = getByTestId('has-new-files-alert');
+      const newFilesAlert = screen.getByTestId('has-new-files-alert');
 
       expect(within(newFilesAlert)
         .getByText(pasteNotificationsMessages.hasNewFilesTitle.defaultMessage)).toBeInTheDocument();
@@ -1401,13 +1472,11 @@ describe('<CourseUnit />', () => {
 
       userEvent.click(within(newFilesAlert).getByText(/Dismiss/i));
 
-      expect(queryByTestId('has-new-files-alert')).toBeNull();
+      expect(screen.queryByTestId('has-new-files-alert')).toBeNull();
     });
 
     it('displays a notification about conflicting errors after pasting a component', async () => {
-      const {
-        queryByTestId, getByTestId, getByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onGet(getCourseUnitApiUrl(courseId))
@@ -1419,8 +1488,8 @@ describe('<CourseUnit />', () => {
       await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
-      userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
 
       const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock);
       const updatedAncestorsChild = updatedCourseSectionVerticalData.xblock_info.ancestor_info.ancestors[0];
@@ -1441,7 +1510,7 @@ describe('<CourseUnit />', () => {
       global.localStorage.setItem('staticFileNotices', JSON.stringify(clipboardMockResponse.staticFileNotices));
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
       await executeThunk(createNewCourseXBlock(camelCaseObject(postXBlockBody), null, blockId), store.dispatch);
-      const conflictingErrorsAlert = getByTestId('has-conflicting-errors-alert');
+      const conflictingErrorsAlert = screen.getByTestId('has-conflicting-errors-alert');
 
       expect(within(conflictingErrorsAlert)
         .getByText(pasteNotificationsMessages.hasConflictingErrorsTitle.defaultMessage)).toBeInTheDocument();
@@ -1455,13 +1524,11 @@ describe('<CourseUnit />', () => {
 
       userEvent.click(within(conflictingErrorsAlert).getByText(/Dismiss/i));
 
-      expect(queryByTestId('has-conflicting-errors-alert')).toBeNull();
+      expect(screen.queryByTestId('has-conflicting-errors-alert')).toBeNull();
     });
 
     it('displays a notification about error files after pasting a component', async () => {
-      const {
-        queryByTestId, getByTestId, getByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onGet(getCourseUnitApiUrl(courseId))
@@ -1473,8 +1540,8 @@ describe('<CourseUnit />', () => {
       await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
 
-      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
-      userEvent.click(getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: sidebarMessages.actionButtonCopyUnitTitle.defaultMessage }));
+      userEvent.click(screen.getByRole('button', { name: courseSequenceMessages.pasteAsNewUnitLink.defaultMessage }));
 
       const updatedCourseSectionVerticalData = cloneDeep(courseSectionVerticalMock);
       const updatedAncestorsChild = updatedCourseSectionVerticalData.xblock_info.ancestor_info.ancestors[0];
@@ -1495,7 +1562,7 @@ describe('<CourseUnit />', () => {
       global.localStorage.setItem('staticFileNotices', JSON.stringify(clipboardMockResponse.staticFileNotices));
       await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
       await executeThunk(createNewCourseXBlock(camelCaseObject(postXBlockBody), null, blockId), store.dispatch);
-      const errorFilesAlert = getByTestId('has-error-files-alert');
+      const errorFilesAlert = screen.getByTestId('has-error-files-alert');
 
       expect(within(errorFilesAlert)
         .getByText(pasteNotificationsMessages.hasErrorsTitle.defaultMessage)).toBeInTheDocument();
@@ -1504,11 +1571,11 @@ describe('<CourseUnit />', () => {
 
       userEvent.click(within(errorFilesAlert).getByText(/Dismiss/i));
 
-      expect(queryByTestId('has-error-files')).toBeNull();
+      expect(screen.queryByTestId('has-error-files')).toBeNull();
     });
 
     it('should hide the "Paste component" block if canPasteComponent is false', async () => {
-      const { queryByText, queryByRole } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onGet(getCourseVerticalChildrenApiUrl(blockId))
@@ -1519,10 +1586,10 @@ describe('<CourseUnit />', () => {
 
       await executeThunk(fetchCourseVerticalChildrenData(blockId), store.dispatch);
 
-      expect(queryByRole('button', {
+      expect(screen.queryByRole('button', {
         name: messages.pasteButtonText.defaultMessage,
       })).not.toBeInTheDocument();
-      expect(queryByText(
+      expect(screen.queryByText(
         pasteComponentMessages.pasteButtonWhatsInClipboardText.defaultMessage,
       )).not.toBeInTheDocument();
     });
@@ -1556,9 +1623,7 @@ describe('<CourseUnit />', () => {
     });
 
     it('should display "Move Modal" on receive trigger message', async () => {
-      const {
-        getByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       await screen.findByText(unitDisplayName);
 
@@ -1572,15 +1637,12 @@ describe('<CourseUnit />', () => {
       await screen.findByText(
         moveModalMessages.moveModalTitle.defaultMessage.replace('{displayName}', requestData.title),
       );
-      expect(getByRole('button', { name: moveModalMessages.moveModalSubmitButton.defaultMessage })).toBeInTheDocument();
-      expect(getByRole('button', { name: moveModalMessages.moveModalCancelButton.defaultMessage })).toBeInTheDocument();
+      expect(screen.getByRole('button', { name: moveModalMessages.moveModalSubmitButton.defaultMessage })).toBeInTheDocument();
+      expect(screen.getByRole('button', { name: moveModalMessages.moveModalCancelButton.defaultMessage })).toBeInTheDocument();
     });
 
     it('should navigates to xBlock current unit', async () => {
-      const {
-        getByText,
-        getByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       await screen.findByText(unitDisplayName);
 
@@ -1596,7 +1658,7 @@ describe('<CourseUnit />', () => {
       );
 
       const currentSection = courseOutlineInfoMock.child_info.children[1];
-      const currentSectionItemBtn = getByRole('button', {
+      const currentSectionItemBtn = screen.getByRole('button', {
         name: `${currentSection.display_name} ${moveModalMessages.moveModalOutlineItemCurrentLocationText.defaultMessage} ${moveModalMessages.moveModalOutlineItemViewText.defaultMessage}`,
       });
       expect(currentSectionItemBtn).toBeInTheDocument();
@@ -1604,7 +1666,7 @@ describe('<CourseUnit />', () => {
 
       await waitFor(() => {
         const currentSubsection = currentSection.child_info.children[0];
-        const currentSubsectionItemBtn = getByRole('button', {
+        const currentSubsectionItemBtn = screen.getByRole('button', {
           name: `${currentSubsection.display_name} ${moveModalMessages.moveModalOutlineItemCurrentLocationText.defaultMessage} ${moveModalMessages.moveModalOutlineItemViewText.defaultMessage}`,
         });
         expect(currentSubsectionItemBtn).toBeInTheDocument();
@@ -1612,7 +1674,7 @@ describe('<CourseUnit />', () => {
       });
 
       await waitFor(() => {
-        const currentComponentLocationText = getByText(
+        const currentComponentLocationText = screen.getByText(
           moveModalMessages.moveModalOutlineItemCurrentComponentLocationText.defaultMessage,
         );
         expect(currentComponentLocationText).toBeInTheDocument();
@@ -1620,9 +1682,7 @@ describe('<CourseUnit />', () => {
     });
 
     it('should allow move operation and handles it successfully', async () => {
-      const {
-        getByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onPatch(postXBlockBaseApiUrl())
@@ -1646,7 +1706,7 @@ describe('<CourseUnit />', () => {
       );
 
       const currentSection = courseOutlineInfoMock.child_info.children[1];
-      const currentSectionItemBtn = getByRole('button', {
+      const currentSectionItemBtn = screen.getByRole('button', {
         name: `${currentSection.display_name} ${moveModalMessages.moveModalOutlineItemCurrentLocationText.defaultMessage} ${moveModalMessages.moveModalOutlineItemViewText.defaultMessage}`,
       });
       expect(currentSectionItemBtn).toBeInTheDocument();
@@ -1654,7 +1714,7 @@ describe('<CourseUnit />', () => {
 
       const currentSubsection = currentSection.child_info.children[1];
       await waitFor(() => {
-        const currentSubsectionItemBtn = getByRole('button', {
+        const currentSubsectionItemBtn = screen.getByRole('button', {
           name: `${currentSubsection.display_name} ${moveModalMessages.moveModalOutlineItemViewText.defaultMessage}`,
         });
         expect(currentSubsectionItemBtn).toBeInTheDocument();
@@ -1663,14 +1723,14 @@ describe('<CourseUnit />', () => {
 
       await waitFor(() => {
         const currentUnit = currentSubsection.child_info.children[0];
-        const currentUnitItemBtn = getByRole('button', {
+        const currentUnitItemBtn = screen.getByRole('button', {
           name: `${currentUnit.display_name} ${moveModalMessages.moveModalOutlineItemViewText.defaultMessage}`,
         });
         expect(currentUnitItemBtn).toBeInTheDocument();
         userEvent.click(currentUnitItemBtn);
       });
 
-      const moveModalBtn = getByRole('button', {
+      const moveModalBtn = screen.getByRole('button', {
         name: moveModalMessages.moveModalSubmitButton.defaultMessage,
       });
       expect(moveModalBtn).toBeInTheDocument();
@@ -1684,10 +1744,7 @@ describe('<CourseUnit />', () => {
     });
 
     it('should display "Move Confirmation" alert after moving and undo operations', async () => {
-      const {
-        queryByRole,
-        getByText,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onPatch(postXBlockBaseApiUrl())
@@ -1704,18 +1761,18 @@ describe('<CourseUnit />', () => {
 
       simulatePostMessageEvent(messageTypes.rollbackMovedXBlock, { locator: requestData.sourceLocator });
 
-      const dismissButton = queryByRole('button', {
+      const dismissButton = screen.queryByRole('button', {
         name: /dismiss/i, hidden: true,
       });
-      const undoButton = queryByRole('button', {
+      const undoButton = screen.queryByRole('button', {
         name: messages.undoMoveButton.defaultMessage, hidden: true,
       });
-      const newLocationButton = queryByRole('button', {
+      const newLocationButton = screen.queryByRole('button', {
         name: messages.newLocationButton.defaultMessage, hidden: true,
       });
 
-      expect(getByText(messages.alertMoveSuccessTitle.defaultMessage)).toBeInTheDocument();
-      expect(getByText(`${requestData.title} has been moved`)).toBeInTheDocument();
+      expect(screen.getByText(messages.alertMoveSuccessTitle.defaultMessage)).toBeInTheDocument();
+      expect(screen.getByText(`${requestData.title} has been moved`)).toBeInTheDocument();
       expect(dismissButton).toBeInTheDocument();
       expect(undoButton).toBeInTheDocument();
       expect(newLocationButton).toBeInTheDocument();
@@ -1723,9 +1780,9 @@ describe('<CourseUnit />', () => {
       userEvent.click(undoButton);
 
       await waitFor(() => {
-        expect(getByText(messages.alertMoveCancelTitle.defaultMessage)).toBeInTheDocument();
+        expect(screen.getByText(messages.alertMoveCancelTitle.defaultMessage)).toBeInTheDocument();
       });
-      expect(getByText(
+      expect(screen.getByText(
         messages.alertMoveCancelDescription.defaultMessage.replace('{title}', requestData.title),
       )).toBeInTheDocument();
       expect(dismissButton).toBeInTheDocument();
@@ -1734,9 +1791,7 @@ describe('<CourseUnit />', () => {
     });
 
     it('should navigate to new location by button click', async () => {
-      const {
-        queryByRole,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       axiosMock
         .onPatch(postXBlockBaseApiUrl())
@@ -1751,7 +1806,7 @@ describe('<CourseUnit />', () => {
         callbackFn: requestData.callbackFn,
       }), store.dispatch);
 
-      const newLocationButton = queryByRole('button', {
+      const newLocationButton = screen.queryByRole('button', {
         name: messages.newLocationButton.defaultMessage, hidden: true,
       });
       userEvent.click(newLocationButton);
@@ -1764,16 +1819,14 @@ describe('<CourseUnit />', () => {
 
   describe('XBlock restrict access', () => {
     it('opens xblock restrict access modal successfully', async () => {
-      const {
-        getByTitle, getByTestId,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       const modalSubtitleText = configureModalMessages.restrictAccessTo.defaultMessage;
       const modalCancelBtnText = configureModalMessages.cancelButton.defaultMessage;
       const modalSaveBtnText = configureModalMessages.saveButton.defaultMessage;
 
       await waitFor(() => {
-        const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+        const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
         const usageId = courseVerticalChildrenMock.children[0].block_id;
         expect(iframe).toBeInTheDocument();
 
@@ -1783,7 +1836,7 @@ describe('<CourseUnit />', () => {
       });
 
       await waitFor(() => {
-        const configureModal = getByTestId('configure-modal');
+        const configureModal = screen.getByTestId('configure-modal');
 
         expect(within(configureModal).getByText(modalSubtitleText)).toBeInTheDocument();
         expect(within(configureModal).getByRole('button', { name: modalCancelBtnText })).toBeInTheDocument();
@@ -1792,12 +1845,10 @@ describe('<CourseUnit />', () => {
     });
 
     it('closes xblock restrict access modal when cancel button is clicked', async () => {
-      const {
-        getByTitle, queryByTestId, getByTestId,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       await waitFor(() => {
-        const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+        const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
         expect(iframe).toBeInTheDocument();
         simulatePostMessageEvent(messageTypes.manageXBlockAccess, {
           usageId: courseVerticalChildrenMock.children[0].block_id,
@@ -1805,7 +1856,7 @@ describe('<CourseUnit />', () => {
       });
 
       await waitFor(() => {
-        const configureModal = getByTestId('configure-modal');
+        const configureModal = screen.getByTestId('configure-modal');
         expect(configureModal).toBeInTheDocument();
         userEvent.click(within(configureModal).getByRole('button', {
           name: configureModalMessages.cancelButton.defaultMessage,
@@ -1813,7 +1864,7 @@ describe('<CourseUnit />', () => {
         expect(handleConfigureSubmitMock).not.toHaveBeenCalled();
       });
 
-      expect(queryByTestId('configure-modal')).not.toBeInTheDocument();
+      expect(screen.queryByTestId('configure-modal')).not.toBeInTheDocument();
     });
 
     it('handles submit xblock restrict access data when save button is clicked', async () => {
@@ -1824,15 +1875,13 @@ describe('<CourseUnit />', () => {
         })
         .reply(200, { dummy: 'value' });
 
-      const {
-        getByTitle, getByRole, getByTestId, queryByTestId,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       const accessGroupName1 = userPartitionInfoFormatted.selectablePartitions[0].groups[0].name;
       const accessGroupName2 = userPartitionInfoFormatted.selectablePartitions[0].groups[1].name;
 
       await waitFor(() => {
-        const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+        const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
         expect(iframe).toBeInTheDocument();
       });
 
@@ -1842,13 +1891,13 @@ describe('<CourseUnit />', () => {
         });
       });
 
-      const configureModal = await waitFor(() => getByTestId('configure-modal'));
+      const configureModal = await waitFor(() => screen.getByTestId('configure-modal'));
       expect(configureModal).toBeInTheDocument();
 
       expect(within(configureModal).queryByText(accessGroupName1)).not.toBeInTheDocument();
       expect(within(configureModal).queryByText(accessGroupName2)).not.toBeInTheDocument();
 
-      const restrictAccessSelect = getByRole('combobox', {
+      const restrictAccessSelect = screen.getByRole('combobox', {
         name: configureModalMessages.restrictAccessTo.defaultMessage,
       });
 
@@ -1878,17 +1927,17 @@ describe('<CourseUnit />', () => {
         expect(axiosMock.history.post[0].url).toBe(getXBlockBaseApiUrl(id));
       });
 
-      expect(queryByTestId('configure-modal')).not.toBeInTheDocument();
+      expect(screen.queryByTestId('configure-modal')).not.toBeInTheDocument();
     });
   });
 
   const checkLegacyEditModalOnEditMessage = async () => {
-    const { getByTitle, getByTestId } = render(<RootWrapper />);
+    render(<RootWrapper />);
 
     await waitFor(() => {
-      const editButton = getByTestId('header-edit-button');
+      const editButton = screen.getByTestId('header-edit-button');
       expect(editButton).toBeInTheDocument();
-      const xblocksIframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const xblocksIframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(xblocksIframe).toBeInTheDocument();
       userEvent.click(editButton);
     });
@@ -2075,46 +2124,65 @@ describe('<CourseUnit />', () => {
     });
 
     it('should render split test content page correctly', async () => {
-      const {
-        getByText,
-        getByRole,
-        queryByRole,
-        getByTestId,
-        queryByText,
-      } = render(<RootWrapper />);
+      render(<RootWrapper />);
 
       const currentSectionName = courseUnitIndexMock.ancestor_info.ancestors[1].display_name;
       const currentSubSectionName = courseUnitIndexMock.ancestor_info.ancestors[1].display_name;
       const helpLinkUrl = 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_components.html#components-that-contain-other-components';
 
       waitFor(() => {
-        const unitHeaderTitle = getByTestId('unit-header-title');
-        expect(getByText(unitDisplayName)).toBeInTheDocument();
+        const unitHeaderTitle = screen.getByTestId('unit-header-title');
+        expect(screen.getByText(unitDisplayName)).toBeInTheDocument();
         expect(within(unitHeaderTitle).getByRole('button', { name: headerTitleMessages.altButtonEdit.defaultMessage })).toBeInTheDocument();
         expect(within(unitHeaderTitle).getByRole('button', { name: headerTitleMessages.altButtonSettings.defaultMessage })).toBeInTheDocument();
-        expect(getByRole('button', { name: currentSectionName })).toBeInTheDocument();
-        expect(getByRole('button', { name: currentSubSectionName })).toBeInTheDocument();
+        expect(screen.getByRole('button', { name: currentSectionName })).toBeInTheDocument();
+        expect(screen.getByRole('button', { name: currentSubSectionName })).toBeInTheDocument();
 
-        expect(queryByRole('heading', { name: addComponentMessages.title.defaultMessage })).not.toBeInTheDocument();
-        expect(queryByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage })).not.toBeInTheDocument();
-        expect(queryByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage })).not.toBeInTheDocument();
+        expect(screen.queryByRole('heading', { name: addComponentMessages.title.defaultMessage })).not.toBeInTheDocument();
+        expect(screen.queryByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage })).not.toBeInTheDocument();
+        expect(screen.queryByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage })).not.toBeInTheDocument();
 
-        expect(queryByRole('heading', { name: /unit tags/i })).not.toBeInTheDocument();
-        expect(queryByRole('heading', { name: /unit location/i })).not.toBeInTheDocument();
+        expect(screen.queryByRole('heading', { name: /unit tags/i })).not.toBeInTheDocument();
+        expect(screen.queryByRole('heading', { name: /unit location/i })).not.toBeInTheDocument();
 
         // Sidebar
         const sidebarContent = [
-          { query: queryByRole, type: 'heading', name: sidebarMessages.sidebarSplitTestAddComponentTitle.defaultMessage },
-          { query: queryByText, name: sidebarMessages.sidebarSplitTestSelectComponentType.defaultMessage.replaceAll('{bold_tag}', '') },
-          { query: queryByText, name: sidebarMessages.sidebarSplitTestComponentAdded.defaultMessage },
-          { query: queryByRole, type: 'heading', name: sidebarMessages.sidebarSplitTestEditComponentTitle.defaultMessage },
-          { query: queryByText, name: sidebarMessages.sidebarSplitTestEditComponentInstruction.defaultMessage.replaceAll('{bold_tag}', '') },
-          { query: queryByRole, type: 'heading', name: sidebarMessages.sidebarSplitTestReorganizeComponentTitle.defaultMessage },
-          { query: queryByText, name: sidebarMessages.sidebarSplitTestReorganizeComponentInstruction.defaultMessage },
-          { query: queryByText, name: sidebarMessages.sidebarSplitTestReorganizeGroupsInstruction.defaultMessage },
-          { query: queryByRole, type: 'heading', name: sidebarMessages.sidebarSplitTestExperimentComponentTitle.defaultMessage },
-          { query: queryByText, name: sidebarMessages.sidebarSplitTestExperimentComponentInstruction.defaultMessage },
-          { query: queryByRole, type: 'link', name: sidebarMessages.sidebarSplitTestLearnMoreLinkLabel.defaultMessage },
+          { query: screen.queryByRole, type: 'heading', name: sidebarMessages.sidebarSplitTestAddComponentTitle.defaultMessage },
+          { query: screen.queryByText, name: sidebarMessages.sidebarSplitTestSelectComponentType.defaultMessage.replaceAll('{bold_tag}', '') },
+          { query: screen.queryByText, name: sidebarMessages.sidebarSplitTestComponentAdded.defaultMessage },
+          { query: screen.queryByRole, type: 'heading', name: sidebarMessages.sidebarSplitTestEditComponentTitle.defaultMessage },
+          {
+            query: screen.queryByText,
+            name: sidebarMessages.sidebarSplitTestEditComponentInstruction.defaultMessage
+              .replaceAll('{bold_tag}', ''),
+          },
+          {
+            query: screen.queryByRole,
+            type: 'heading',
+            name: sidebarMessages.sidebarSplitTestReorganizeComponentTitle.defaultMessage,
+          },
+          {
+            query: screen.queryByText,
+            name: sidebarMessages.sidebarSplitTestReorganizeComponentInstruction.defaultMessage,
+          },
+          {
+            query: screen.queryByText,
+            name: sidebarMessages.sidebarSplitTestReorganizeGroupsInstruction.defaultMessage,
+          },
+          {
+            query: screen.queryByRole,
+            type: 'heading',
+            name: sidebarMessages.sidebarSplitTestExperimentComponentTitle.defaultMessage,
+          },
+          {
+            query: screen.queryByText,
+            name: sidebarMessages.sidebarSplitTestExperimentComponentInstruction.defaultMessage,
+          },
+          {
+            query: screen.queryByRole,
+            type: 'link',
+            name: sidebarMessages.sidebarSplitTestLearnMoreLinkLabel.defaultMessage,
+          },
         ];
 
         sidebarContent.forEach(({ query, type, name }) => {
@@ -2122,7 +2190,7 @@ describe('<CourseUnit />', () => {
         });
 
         expect(
-          queryByRole('link', { name: sidebarMessages.sidebarSplitTestLearnMoreLinkLabel.defaultMessage }),
+          screen.queryByRole('link', { name: sidebarMessages.sidebarSplitTestLearnMoreLinkLabel.defaultMessage }),
         ).toHaveAttribute('href', helpLinkUrl);
       });
     });
@@ -2135,7 +2203,7 @@ describe('<CourseUnit />', () => {
   });
 
   it('renders and navigates to the new HTML XBlock editor after xblock duplicating', async () => {
-    const { getByTitle } = render(<RootWrapper />);
+    render(<RootWrapper />);
     const updatedCourseVerticalChildrenMock = JSON.parse(JSON.stringify(courseVerticalChildrenMock));
     const targetBlockId = updatedCourseVerticalChildrenMock.children[1].block_id;
 
@@ -2151,7 +2219,7 @@ describe('<CourseUnit />', () => {
     await executeThunk(fetchCourseVerticalChildrenData(blockId), store.dispatch);
 
     await waitFor(() => {
-      const iframe = getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
+      const iframe = screen.getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);
       expect(iframe).toBeInTheDocument();
       simulatePostMessageEvent(messageTypes.currentXBlockId, {
         id: targetBlockId,
diff --git a/src/course-unit/add-component/AddComponent.jsx b/src/course-unit/add-component/AddComponent.jsx
index 74431791ab..9e0fd850f7 100644
--- a/src/course-unit/add-component/AddComponent.jsx
+++ b/src/course-unit/add-component/AddComponent.jsx
@@ -8,6 +8,7 @@ import {
 } from '@openedx/paragon';
 
 import { getCourseSectionVertical } from '../data/selectors';
+import { getWaffleFlags } from '../../data/selectors';
 import { COMPONENT_TYPES } from '../../generic/block-type-utils/constants';
 import ComponentModalView from './add-component-modals/ComponentModalView';
 import AddComponentButton from './add-component-btn';
@@ -16,6 +17,7 @@ import { ComponentPicker } from '../../library-authoring/component-picker';
 import { messageTypes } from '../constants';
 import { useIframe } from '../../generic/hooks/context/hooks';
 import { useEventListener } from '../../generic/hooks';
+import VideoSelectorPage from '../../editors/VideoSelectorPage';
 import EditorPage from '../../editors/EditorPage';
 
 const AddComponent = ({
@@ -32,6 +34,7 @@ const AddComponent = ({
   const { componentTemplates = {} } = useSelector(getCourseSectionVertical);
   const blockId = addComponentTemplateData.parentLocator || parentLocator;
   const [isAddLibraryContentModalOpen, showAddLibraryContentModal, closeAddLibraryContentModal] = useToggle();
+  const [isVideoSelectorModalOpen, showVideoSelectorModal, closeVideoSelectorModal] = useToggle();
   const [isXBlockEditorModalOpen, showXBlockEditorModal, closeXBlockEditorModal] = useToggle();
 
   const [blockType, setBlockType] = useState(null);
@@ -41,6 +44,7 @@ const AddComponent = ({
   const [selectedComponents, setSelectedComponents] = useState([]);
   const [usageId, setUsageId] = useState(null);
   const { sendMessageToIframe } = useIframe();
+  const { useVideoGalleryFlow } = useSelector(getWaffleFlags);
 
   const receiveMessage = useCallback(({ data: { type, payload } }) => {
     if (type === messageTypes.showMultipleComponentPicker) {
@@ -61,8 +65,9 @@ const AddComponent = ({
 
   const onXBlockSave = useCallback(/* istanbul ignore next */ () => {
     closeXBlockEditorModal();
+    closeVideoSelectorModal();
     sendMessageToIframe(messageTypes.refreshXBlock, null);
-  }, [closeXBlockEditorModal, sendMessageToIframe]);
+  }, [closeXBlockEditorModal, closeVideoSelectorModal, sendMessageToIframe]);
 
   const handleLibraryV2Selection = useCallback((selection) => {
     handleCreateNewCourseXBlock({
@@ -80,7 +85,6 @@ const AddComponent = ({
       case COMPONENT_TYPES.dragAndDrop:
         handleCreateNewCourseXBlock({ type, parentLocator: blockId });
         break;
-      case COMPONENT_TYPES.video:
       case COMPONENT_TYPES.problem:
         handleCreateNewCourseXBlock({ type, parentLocator: blockId }, ({ courseKey, locator }) => {
           setCourseId(courseKey);
@@ -89,6 +93,21 @@ const AddComponent = ({
           showXBlockEditorModal();
         });
         break;
+      case COMPONENT_TYPES.video:
+        handleCreateNewCourseXBlock(
+          { type, parentLocator: blockId },
+          /* istanbul ignore next */ ({ courseKey, locator }) => {
+            setCourseId(courseKey);
+            setBlockType(type);
+            setNewBlockId(locator);
+            if (useVideoGalleryFlow) {
+              showVideoSelectorModal();
+            } else {
+              showXBlockEditorModal();
+            }
+          },
+        );
+        break;
         // TODO: The library functional will be a bit different of current legacy (CMS)
         //  behaviour and this ticket is on hold (blocked by other development team).
       case COMPONENT_TYPES.library:
@@ -215,6 +234,24 @@ const AddComponent = ({
             onChangeComponentSelection={setSelectedComponents}
           />
         </StandardModal>
+        <StandardModal
+          title={intl.formatMessage(messages.videoPickerModalTitle)}
+          isOpen={isVideoSelectorModalOpen}
+          onClose={closeVideoSelectorModal}
+          isOverflowVisible={false}
+          size="xl"
+        >
+          <div className="selector-page">
+            <VideoSelectorPage
+              blockId={newBlockId}
+              courseId={courseId}
+              studioEndpointUrl={getConfig().STUDIO_BASE_URL}
+              lmsEndpointUrl={getConfig().LMS_BASE_URL}
+              onCancel={closeVideoSelectorModal}
+              returnFunction={/* istanbul ignore next */ () => onXBlockSave}
+            />
+          </div>
+        </StandardModal>
         <StandardModal
           title={intl.formatMessage(messages.blockEditorModalTitle)}
           isOpen={isXBlockEditorModalOpen}
diff --git a/src/course-unit/xblock-container-iframe/hooks/types.ts b/src/course-unit/xblock-container-iframe/hooks/types.ts
index 9a8ff012d7..3c54a90f29 100644
--- a/src/course-unit/xblock-container-iframe/hooks/types.ts
+++ b/src/course-unit/xblock-container-iframe/hooks/types.ts
@@ -4,7 +4,7 @@ export type UseMessageHandlersTypes = {
   setIframeOffset: (height: number) => void;
   handleDeleteXBlock: (usageId: string) => void;
   handleScrollToXBlock: (scrollOffset: number) => void;
-  handleDuplicateXBlock: (blockType: string, usageId: string) => void;
+  handleDuplicateXBlock: (usageId: string) => void;
   handleEditXBlock: (blockType: string, usageId: string) => void;
   handleManageXBlockAccess: (usageId: string) => void;
   handleShowLegacyEditXBlockModal: (id: string) => void;
diff --git a/src/course-unit/xblock-container-iframe/hooks/useMessageHandlers.tsx b/src/course-unit/xblock-container-iframe/hooks/useMessageHandlers.tsx
index b5e50b427b..daae1b8223 100644
--- a/src/course-unit/xblock-container-iframe/hooks/useMessageHandlers.tsx
+++ b/src/course-unit/xblock-container-iframe/hooks/useMessageHandlers.tsx
@@ -37,7 +37,7 @@ export const useMessageHandlers = ({
     [messageTypes.copyXBlock]: ({ usageId }) => copyToClipboard(usageId),
     [messageTypes.deleteXBlock]: ({ usageId }) => handleDeleteXBlock(usageId),
     [messageTypes.newXBlockEditor]: ({ blockType, usageId }) => handleEditXBlock(blockType, usageId),
-    [messageTypes.duplicateXBlock]: ({ blockType, usageId }) => handleDuplicateXBlock(blockType, usageId),
+    [messageTypes.duplicateXBlock]: ({ usageId }) => handleDuplicateXBlock(usageId),
     [messageTypes.manageXBlockAccess]: ({ usageId }) => handleManageXBlockAccess(usageId),
     [messageTypes.scrollToXBlock]: debounce(({ scrollOffset }) => handleScrollToXBlock(scrollOffset), 1000),
     [messageTypes.toggleCourseXBlockDropdown]: ({
diff --git a/src/course-unit/xblock-container-iframe/index.tsx b/src/course-unit/xblock-container-iframe/index.tsx
index e780e3485a..0c1ab91e24 100644
--- a/src/course-unit/xblock-container-iframe/index.tsx
+++ b/src/course-unit/xblock-container-iframe/index.tsx
@@ -4,8 +4,7 @@ import {
 } from 'react';
 import { useIntl } from '@edx/frontend-platform/i18n';
 import { useToggle, Sheet, StandardModal } from '@openedx/paragon';
-import { useDispatch } from 'react-redux';
-import { useNavigate } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
 
 import {
   hideProcessingNotification,
@@ -14,9 +13,9 @@ import {
 import DeleteModal from '../../generic/delete-modal/DeleteModal';
 import ConfigureModal from '../../generic/configure-modal/ConfigureModal';
 import ModalIframe from '../../generic/modal-iframe';
+import { getWaffleFlags } from '../../data/selectors';
 import { IFRAME_FEATURE_POLICY } from '../../constants';
 import ContentTagsDrawer from '../../content-tags-drawer/ContentTagsDrawer';
-import supportedEditors from '../../editors/supportedEditors';
 import { useIframe } from '../../generic/hooks/context/hooks';
 import {
   fetchCourseSectionVerticalData,
@@ -36,6 +35,7 @@ import messages from './messages';
 import { useIframeBehavior } from '../../generic/hooks/useIframeBehavior';
 import { useIframeContent } from '../../generic/hooks/useIframeContent';
 import { useIframeMessages } from '../../generic/hooks/useIframeMessages';
+import VideoSelectorPage from '../../editors/VideoSelectorPage';
 import EditorPage from '../../editors/EditorPage';
 
 const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
@@ -43,12 +43,13 @@ const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
 }) => {
   const intl = useIntl();
   const dispatch = useDispatch();
-  const navigate = useNavigate();
 
   const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useToggle(false);
   const [isConfigureModalOpen, openConfigureModal, closeConfigureModal] = useToggle(false);
+  const [isVideoSelectorModalOpen, showVideoSelectorModal, closeVideoSelectorModal] = useToggle();
   const [isXBlockEditorModalOpen, showXBlockEditorModal, closeXBlockEditorModal] = useToggle();
   const [blockType, setBlockType] = useState<string>('');
+  const { useVideoGalleryFlow } = useSelector(getWaffleFlags);
   const [newBlockId, setNewBlockId] = useState<string>('');
   const [accessManagedXBlockData, setAccessManagedXBlockData] = useState<AccessManagedXBlockDataTypes | {}>({});
   const [iframeOffset, setIframeOffset] = useState(0);
@@ -71,24 +72,25 @@ const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
 
   const onXBlockSave = useCallback(/* istanbul ignore next */ () => {
     closeXBlockEditorModal();
+    closeVideoSelectorModal();
     sendMessageToIframe(messageTypes.refreshXBlock, null);
-  }, [closeXBlockEditorModal, sendMessageToIframe]);
+  }, [closeXBlockEditorModal, closeVideoSelectorModal, sendMessageToIframe]);
 
   const handleEditXBlock = useCallback((type: string, id: string) => {
     setBlockType(type);
     setNewBlockId(id);
-    showXBlockEditorModal();
-  }, [showXBlockEditorModal]);
+    if (type === 'video' && useVideoGalleryFlow) {
+      showVideoSelectorModal();
+    } else {
+      showXBlockEditorModal();
+    }
+  }, [showVideoSelectorModal, showXBlockEditorModal]);
 
   const handleDuplicateXBlock = useCallback(
-    (type: string, usageId: string) => {
+    (usageId: string) => {
       unitXBlockActions.handleDuplicate(usageId);
-      if (supportedEditors[type]) {
-        // istanbul ignore next
-        handleEditXBlock(type, usageId);
-      }
     },
-    [unitXBlockActions, courseId, navigate],
+    [unitXBlockActions, courseId],
   );
 
   const handleDeleteXBlock = (usageId: string) => {
@@ -198,6 +200,24 @@ const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
         close={closeDeleteModal}
         onDeleteSubmit={onDeleteSubmit}
       />
+      <StandardModal
+        title={intl.formatMessage(messages.videoPickerModalTitle)}
+        isOpen={isVideoSelectorModalOpen}
+        onClose={closeVideoSelectorModal}
+        isOverflowVisible={false}
+        size="xl"
+      >
+        <div className="selector-page">
+          <VideoSelectorPage
+            blockId={newBlockId}
+            courseId={courseId}
+            studioEndpointUrl={getConfig().STUDIO_BASE_URL}
+            lmsEndpointUrl={getConfig().LMS_BASE_URL}
+            onCancel={closeVideoSelectorModal}
+            returnFunction={/* istanbul ignore next */ () => onXBlockSave}
+          />
+        </div>
+      </StandardModal>
       <StandardModal
         title={intl.formatMessage(messages.blockEditorModalTitle)}
         isOpen={isXBlockEditorModalOpen}
diff --git a/src/data/slice.js b/src/data/slice.js
index 5953d6d3fa..4f858916b8 100644
--- a/src/data/slice.js
+++ b/src/data/slice.js
@@ -26,6 +26,7 @@ const slice = createSlice({
       useNewCertificatesPage: true,
       useNewTextbooksPage: true,
       useNewGroupConfigurationsPage: true,
+      useVideoGalleryFlow: false,
     },
   },
   reducers: {
diff --git a/src/editors/VideoSelector.jsx b/src/editors/VideoSelector.jsx
index d6ea5dbba1..4d020066fa 100644
--- a/src/editors/VideoSelector.jsx
+++ b/src/editors/VideoSelector.jsx
@@ -9,6 +9,8 @@ const VideoSelector = ({
   learningContextId,
   lmsEndpointUrl,
   studioEndpointUrl,
+  returnFunction,
+  onCancel,
 }) => {
   const dispatch = useDispatch();
   const loading = hooks.useInitializeApp({
@@ -26,7 +28,7 @@ const VideoSelector = ({
     return null;
   }
   return (
-    <VideoGallery />
+    <VideoGallery returnFunction={returnFunction} onCancel={onCancel} />
   );
 };
 
@@ -35,6 +37,8 @@ VideoSelector.propTypes = {
   learningContextId: PropTypes.string.isRequired,
   lmsEndpointUrl: PropTypes.string.isRequired,
   studioEndpointUrl: PropTypes.string.isRequired,
+  returnFunction: PropTypes.func,
+  onCancel: PropTypes.func,
 };
 
 export default VideoSelector;
diff --git a/src/editors/VideoSelectorPage.jsx b/src/editors/VideoSelectorPage.jsx
index 0d9609b045..1f2fe1a768 100644
--- a/src/editors/VideoSelectorPage.jsx
+++ b/src/editors/VideoSelectorPage.jsx
@@ -10,6 +10,8 @@ const VideoSelectorPage = ({
   courseId,
   lmsEndpointUrl,
   studioEndpointUrl,
+  returnFunction,
+  onCancel,
 }) => (
   <Provider store={store}>
     <ErrorBoundary
@@ -24,6 +26,8 @@ const VideoSelectorPage = ({
           learningContextId: courseId,
           lmsEndpointUrl,
           studioEndpointUrl,
+          returnFunction,
+          onCancel,
         }}
       />
     </ErrorBoundary>
@@ -42,6 +46,8 @@ VideoSelectorPage.propTypes = {
   courseId: PropTypes.string,
   lmsEndpointUrl: PropTypes.string,
   studioEndpointUrl: PropTypes.string,
+  returnFunction: PropTypes.func,
+  onCancel: PropTypes.func,
 };
 
 export default VideoSelectorPage;
diff --git a/src/editors/containers/VideoGallery/hooks.js b/src/editors/containers/VideoGallery/hooks.js
index 648db7a641..7b392463fa 100644
--- a/src/editors/containers/VideoGallery/hooks.js
+++ b/src/editors/containers/VideoGallery/hooks.js
@@ -86,6 +86,7 @@ export const filterList = ({
 export const useVideoListProps = ({
   searchSortProps,
   videos,
+  returnFunction,
 }) => {
   const [highlighted, setHighlighted] = React.useState(null);
   const [
@@ -128,7 +129,10 @@ export const useVideoListProps = ({
     },
     selectBtnProps: {
       onClick: () => {
-        if (highlighted) {
+        /* istanbul ignore next */
+        if (returnFunction) {
+          returnFunction()();
+        } else if (highlighted) {
           navigateTo(`/course/${learningContextId}/editor/video/${blockId}?selectedVideoId=${highlighted}`);
         } else {
           setShowSelectVideoError(true);
@@ -138,10 +142,15 @@ export const useVideoListProps = ({
   };
 };
 
-export const useVideoUploadHandler = ({ replace }) => {
+export const useVideoUploadHandler = ({ replace, uploadHandler }) => {
   const learningContextId = useSelector(selectors.app.learningContextId);
   const blockId = useSelector(selectors.app.blockId);
   const path = `/course/${learningContextId}/editor/video_upload/${blockId}`;
+  if (uploadHandler) {
+    return () => {
+      uploadHandler();
+    };
+  }
   if (replace) {
     return () => window.location.replace(path);
   }
@@ -191,11 +200,12 @@ export const getstatusBadgeVariant = ({ status }) => {
 
 export const getStatusMessage = ({ status }) => Object.values(filterMessages).find((m) => m.defaultMessage === status);
 
-export const useVideoProps = ({ videos }) => {
+export const useVideoProps = ({ videos, uploadHandler, returnFunction }) => {
   const searchSortProps = useSearchAndSortProps();
   const videoList = useVideoListProps({
     searchSortProps,
     videos,
+    returnFunction,
   });
   const {
     galleryError,
@@ -203,7 +213,7 @@ export const useVideoProps = ({ videos }) => {
     inputError,
     selectBtnProps,
   } = videoList;
-  const fileInput = { click: useVideoUploadHandler({ replace: false }) };
+  const fileInput = { click: useVideoUploadHandler({ replace: false, uploadHandler }) };
 
   return {
     galleryError,
diff --git a/src/editors/containers/VideoGallery/index.jsx b/src/editors/containers/VideoGallery/index.jsx
index 5f5a8a8ca8..ac25c85268 100644
--- a/src/editors/containers/VideoGallery/index.jsx
+++ b/src/editors/containers/VideoGallery/index.jsx
@@ -1,5 +1,10 @@
-import React, { useEffect } from 'react';
-import { Image } from '@openedx/paragon';
+import React, { useCallback, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import { useIntl } from '@edx/frontend-platform/i18n';
+import {
+  Image, useToggle, StandardModal,
+} from '@openedx/paragon';
+import { useSearchParams } from 'react-router-dom';
 import { useSelector } from 'react-redux';
 import { selectors } from '../../data/redux';
 import * as hooks from './hooks';
@@ -8,8 +13,11 @@ import { acceptedImgKeys } from './utils';
 import messages from './messages';
 import { RequestKeys } from '../../data/constants/requests';
 import videoThumbnail from '../../data/images/videoThumbnail.svg';
+import VideoUploadEditor from '../VideoUploadEditor';
+import VideoEditor from '../VideoEditor';
 
-const VideoGallery = () => {
+const VideoGallery = ({ returnFunction, onCancel }) => {
+  const intl = useIntl();
   const rawVideos = useSelector(selectors.app.videos);
   const isLoaded = useSelector(
     (state) => selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchVideos }),
@@ -21,14 +29,27 @@ const VideoGallery = () => {
     (state) => selectors.requests.isFailed(state, { requestKey: RequestKeys.uploadVideo }),
   );
   const videos = hooks.buildVideos({ rawVideos });
-  const handleVideoUpload = hooks.useVideoUploadHandler({ replace: true });
+  const [isVideoUploadModalOpen, showVideoUploadModal, closeVideoUploadModal] = useToggle();
+  const [isVideoEditorModalOpen, showVideoEditorModal, closeVideoEditorModal] = useToggle();
+  const setSearchParams = useSearchParams()[1];
 
   useEffect(() => {
-    // If no videos exists redirects to the video upload screen
+    // If no videos exists opens to the video upload modal
     if (isLoaded && videos.length === 0) {
-      handleVideoUpload();
+      showVideoUploadModal();
     }
   }, [isLoaded]);
+
+  const onVideoUpload = useCallback((videoUrl) => {
+    closeVideoUploadModal();
+    showVideoEditorModal();
+    setSearchParams({ selectedVideoUrl: videoUrl });
+  }, [closeVideoUploadModal, showVideoEditorModal, setSearchParams]);
+
+  const uploadHandler = useCallback(() => {
+    showVideoUploadModal();
+  });
+
   const {
     galleryError,
     inputError,
@@ -36,7 +57,7 @@ const VideoGallery = () => {
     galleryProps,
     searchSortProps,
     selectBtnProps,
-  } = hooks.useVideoProps({ videos });
+  } = hooks.useVideoProps({ videos, uploadHandler, returnFunction });
   const handleCancel = hooks.useCancelHandler();
 
   const modalMessages = {
@@ -60,8 +81,8 @@ const VideoGallery = () => {
       <SelectionModal
         {...{
           isOpen: true,
-          close: handleCancel,
-          size: 'fullscreen',
+          close: onCancel || handleCancel,
+          size: 'xl',
           isFullscreenScroll: false,
           galleryError,
           inputError,
@@ -79,10 +100,34 @@ const VideoGallery = () => {
           isFetchError,
         }}
       />
+      <StandardModal
+        title={intl.formatMessage(messages.videoUploadModalTitle)}
+        isOpen={isVideoUploadModalOpen}
+        onClose={closeVideoUploadModal}
+        isOverflowVisible={false}
+        size="xl"
+        hasCloseButton={false}
+      >
+        <div className="editor-page">
+          <VideoUploadEditor
+            onUpload={onVideoUpload}
+            onClose={closeVideoUploadModal}
+          />
+        </div>
+      </StandardModal>
+      {isVideoEditorModalOpen && (
+        <VideoEditor
+          onClose={closeVideoEditorModal}
+          returnFunction={returnFunction}
+        />
+      )}
     </div>
   );
 };
 
-VideoGallery.propTypes = {};
+VideoGallery.propTypes = {
+  onCancel: PropTypes.func,
+  returnFunction: PropTypes.func,
+};
 
 export default VideoGallery;
diff --git a/src/editors/containers/VideoGallery/index.test.jsx b/src/editors/containers/VideoGallery/index.test.jsx
index 1cffffea82..6e45a02d27 100644
--- a/src/editors/containers/VideoGallery/index.test.jsx
+++ b/src/editors/containers/VideoGallery/index.test.jsx
@@ -6,6 +6,8 @@ import React from 'react';
 import {
   act, fireEvent, render, screen,
 } from '@testing-library/react';
+import * as reactRouterDom from 'react-router-dom';
+import * as reduxThunks from '../../data/redux';
 
 import VideoGallery from './index';
 
@@ -120,11 +122,10 @@ describe('VideoGallery', () => {
         expect(screen.getByText(video.client_video_id)).toBeInTheDocument()
       ));
     });
-    it('navigates to video upload page when there are no videos', async () => {
-      expect(window.location.replace).not.toHaveBeenCalled();
+    it('renders video upload modal when there are no videos', async () => {
       updateState({ videos: [] });
       await renderComponent();
-      expect(window.location.replace).toHaveBeenCalled();
+      expect(screen.getByRole('heading', { name: /upload or embed a new video/i })).toBeInTheDocument();
     });
     it.each([
       [/newest/i, [2, 1, 3]],
@@ -191,5 +192,36 @@ describe('VideoGallery', () => {
       expect(screen.queryByText('client_id_1')).not.toBeInTheDocument();
       expect(screen.queryByText('client_id_3')).not.toBeInTheDocument();
     });
+
+    it('calls onVideoUpload correctly when a video is uploaded', async () => {
+      // Mock useSearchParams
+      const setSearchParams = jest.fn();
+      jest.spyOn(reactRouterDom, 'useSearchParams').mockReturnValue([{}, setSearchParams]);
+
+      // Mock the uploadVideo thunk to immediately call postUploadRedirect
+      jest.spyOn(reduxThunks.thunkActions.video, 'uploadVideo').mockImplementation(
+        ({ postUploadRedirect }) => () => {
+          if (postUploadRedirect) {
+            postUploadRedirect('http://test.video/url.mp4');
+          }
+          return { type: 'MOCK_UPLOAD_VIDEO' };
+        },
+      );
+
+      await renderComponent();
+
+      // Open the upload modal by clicking the button
+      const openModalButton = screen.getByRole('button', { name: /upload or embed a new video/i });
+      fireEvent.click(openModalButton);
+
+      // Wait for the input to appear in the modal
+      const urlInput = await screen.findByPlaceholderText('Paste your video ID or URL');
+      fireEvent.change(urlInput, { target: { value: 'http://test.video/url.mp4' } });
+
+      const submitButton = screen.getByRole('button', { name: /submit/i });
+      fireEvent.click(submitButton);
+
+      expect(setSearchParams).toHaveBeenCalledWith({ selectedVideoUrl: 'http://test.video/url.mp4' });
+    });
   });
 });
diff --git a/src/editors/containers/VideoGallery/messages.js b/src/editors/containers/VideoGallery/messages.js
index e26dd63db3..3dd446b7c9 100644
--- a/src/editors/containers/VideoGallery/messages.js
+++ b/src/editors/containers/VideoGallery/messages.js
@@ -21,7 +21,16 @@ const messages = {
     defaultMessage: 'Upload or embed a new video',
     description: 'Label for upload button',
   },
-
+  videoUploadModalTitle: {
+    id: 'authoring.selectvideomodal.upload.title',
+    defaultMessage: 'Upload or embed a new video',
+    description: 'Label for upload modal',
+  },
+  videoEditorModalTitle: {
+    id: 'authoring.selectvideomodal.edit.title',
+    defaultMessage: 'Edit selected video',
+    description: 'Label for editor modal',
+  },
   // Sort Dropdown
   sortByDateNewest: {
     id: 'authoring.selectvideomodal.sort.datenewest.label',
diff --git a/src/editors/containers/VideoUploadEditor/VideoUploader.jsx b/src/editors/containers/VideoUploadEditor/VideoUploader.jsx
index 028d1c085a..09d943db83 100644
--- a/src/editors/containers/VideoUploadEditor/VideoUploader.jsx
+++ b/src/editors/containers/VideoUploadEditor/VideoUploader.jsx
@@ -10,9 +10,9 @@ import { thunkActions } from '../../data/redux';
 import * as hooks from './hooks';
 import messages from './messages';
 
-const URLUploader = () => {
+const URLUploader = ({ onUpload }) => {
   const [textInputValue, setTextInputValue] = React.useState('');
-  const onURLUpload = hooks.onVideoUpload('selectedVideoUrl');
+  const onURLUpload = hooks.onVideoUpload('selectedVideoUrl', onUpload);
   const intl = useIntl();
   return (
     <div className="d-flex flex-column">
@@ -58,16 +58,16 @@ const URLUploader = () => {
   );
 };
 
-export const VideoUploader = ({ setLoading }) => {
+export const VideoUploader = ({ setLoading, onUpload, onClose }) => {
   const dispatch = useDispatch();
   const intl = useIntl();
-  const goBack = hooks.useHistoryGoBack();
+  const goBack = onClose || hooks.useHistoryGoBack();
 
   const handleProcessUpload = ({ fileData }) => {
     dispatch(thunkActions.video.uploadVideo({
       supportedFiles: [fileData],
       setLoadSpinner: setLoading,
-      postUploadRedirect: hooks.onVideoUpload('selectedVideoId'),
+      postUploadRedirect: hooks.onVideoUpload('selectedVideoId', onUpload),
     }));
   };
 
@@ -85,14 +85,20 @@ export const VideoUploader = ({ setLoading }) => {
       <Dropzone
         accept={{ 'video/*': ['.mp4', '.mov'] }}
         onProcessUpload={handleProcessUpload}
-        inputComponent={<URLUploader />}
+        inputComponent={<URLUploader onUpload={onUpload} />}
       />
     </div>
   );
 };
 
+URLUploader.propTypes = {
+  onUpload: PropTypes.func,
+};
+
 VideoUploader.propTypes = {
   setLoading: PropTypes.func.isRequired,
+  onUpload: PropTypes.func,
+  onClose: PropTypes.func,
 };
 
 export default VideoUploader;
diff --git a/src/editors/containers/VideoUploadEditor/hooks.js b/src/editors/containers/VideoUploadEditor/hooks.js
index a2774d9c60..3cc1f8468e 100644
--- a/src/editors/containers/VideoUploadEditor/hooks.js
+++ b/src/editors/containers/VideoUploadEditor/hooks.js
@@ -11,15 +11,20 @@ export const {
   navigateTo,
 } = appHooks;
 
-export const postUploadRedirect = (storeState, uploadType = 'selectedVideoUrl') => {
+export const postUploadRedirect = (storeState, uploadType = 'selectedVideoUrl', onUpload = null) => {
   const learningContextId = selectors.app.learningContextId(storeState);
   const blockId = selectors.app.blockId(storeState);
+  if (onUpload) {
+    return (videoUrl) => {
+      onUpload(videoUrl, learningContextId, blockId);
+    };
+  }
   return (videoUrl) => navigateTo(`/course/${learningContextId}/editor/video/${blockId}?${uploadType}=${videoUrl}`);
 };
 
-export const onVideoUpload = (uploadType) => {
+export const onVideoUpload = (uploadType, onUpload) => {
   const storeState = store.getState();
-  return module.postUploadRedirect(storeState, uploadType);
+  return module.postUploadRedirect(storeState, uploadType, onUpload);
 };
 
 export const useUploadVideo = async ({
diff --git a/src/editors/containers/VideoUploadEditor/index.jsx b/src/editors/containers/VideoUploadEditor/index.jsx
index ae2be7b5fb..91664d3e0a 100644
--- a/src/editors/containers/VideoUploadEditor/index.jsx
+++ b/src/editors/containers/VideoUploadEditor/index.jsx
@@ -1,17 +1,18 @@
 import React from 'react';
+import PropTypes from 'prop-types';
 import { useIntl } from '@edx/frontend-platform/i18n';
 import { Spinner } from '@openedx/paragon';
 import './index.scss';
 import messages from './messages';
 import { VideoUploader } from './VideoUploader';
 
-const VideoUploadEditor = () => {
+const VideoUploadEditor = ({ onUpload, onClose }) => {
   const [loading, setLoading] = React.useState(false);
   const intl = useIntl();
 
   return (!loading) ? (
     <div className="d-flex marked-area flex-column p-3">
-      <VideoUploader setLoading={setLoading} />
+      <VideoUploader onUpload={onUpload} onClose={onClose} setLoading={setLoading} />
     </div>
   ) : (
     <div style={{
@@ -30,4 +31,9 @@ const VideoUploadEditor = () => {
   );
 };
 
+VideoUploadEditor.propTypes = {
+  onUpload: PropTypes.func,
+  onClose: PropTypes.func,
+};
+
 export default VideoUploadEditor;


Reviews:
user: login: pomegranited
body: :+1: This is working! Thank you @DanielVZ96 .

- [x] I tested this on my tutor dev stack with https://github.com/openedx/edx-platform/pull/36641
  * duplicated blocks in a course -- opens a modal to edit the duplicated block.
  * with `new_core_editors.use_video_gallery_flow` flag enabled, created a video block, and ensured the video gallery is shown. I couldn't test the full edx-val pipeline on my local dev stack, though.
  * with `new_core_editors.use_video_gallery_flow` flag disabled, created a video block, and ensured the normal video editing modal is shown.
- [x] I read through the code
- [x] I checked for accessibility issues by using my keyboard to navigate
- [x] <strike>Includes documentation</strike> N/A
- [x] <strike>User-facing strings are extracted for translation</strike> N/A
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T06:54:31Z
user: login: ChrisChV
body: Looks good! I added some comments
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T19:46:16Z
user: login: DanielVZ96
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T22:56:20Z
user: login: DanielVZ96
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T22:56:25Z

Review comments:
diff_hunk: @@ -829,6 +830,82 @@ describe('<CourseUnit />', () => {
     });
   });
 
+  it('handles creating Video xblock and showing editor modal using videogalleryflow', async () => {
+    const waffleSpy = jest.spyOn(selectors, 'getWaffleFlags').mockReturnValue({ useVideoGalleryFlow: true });
+
+    axiosMock
+      .onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
+      .reply(200, courseCreateXblockMock);
+    const { getByText, queryByRole, getByRole } = render(<RootWrapper />);
path: src/course-unit/CourseUnit.test.jsx
user: login: ChrisChV
body: @DanielVZ96 It's better to use `screen` for this ([ref](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-screen))
created_at: 2025-05-13T18:43:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -829,6 +830,82 @@ describe('<CourseUnit />', () => {
     });
   });
 
+  it('handles creating Video xblock and showing editor modal using videogalleryflow', async () => {
+    const waffleSpy = jest.spyOn(selectors, 'getWaffleFlags').mockReturnValue({ useVideoGalleryFlow: true });
+
+    axiosMock
+      .onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
+      .reply(200, courseCreateXblockMock);
+    const { getByText, queryByRole, getByRole } = render(<RootWrapper />);
+
+    await waitFor(() => {
+      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
+    });
+
+    axiosMock
+      .onPost(getXBlockBaseApiUrl(blockId), {
+        publish: PUBLISH_TYPES.makePublic,
+      })
+      .reply(200, { dummy: 'value' });
+    axiosMock
+      .onGet(getCourseUnitApiUrl(blockId))
+      .reply(200, {
+        ...courseUnitIndexMock,
+        visibility_state: UNIT_VISIBILITY_STATES.live,
+        has_changes: false,
+        published_by: userName,
+      });
+
+    await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
+
+    await waitFor(() => {
+      // check if the sidebar status is Published and Live
+      expect(getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
+      expect(getByText(
+        sidebarMessages.publishLastPublished.defaultMessage
+          .replace('{publishedOn}', courseUnitIndexMock.published_on)
+          .replace('{publishedBy}', userName),
+      )).toBeInTheDocument();
+      expect(queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
path: src/course-unit/CourseUnit.test.jsx
user: login: ChrisChV
body: @DanielVZ96 Avoid to make multiple assertions in a single `waitFor` ([ref](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#having-multiple-assertions-in-a-single-waitfor-callback))
created_at: 2025-05-13T18:46:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -829,6 +830,82 @@ describe('<CourseUnit />', () => {
     });
   });
 
+  it('handles creating Video xblock and showing editor modal using videogalleryflow', async () => {
+    const waffleSpy = jest.spyOn(selectors, 'getWaffleFlags').mockReturnValue({ useVideoGalleryFlow: true });
+
+    axiosMock
+      .onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
+      .reply(200, courseCreateXblockMock);
+    const { getByText, queryByRole, getByRole } = render(<RootWrapper />);
path: src/course-unit/CourseUnit.test.jsx
user: login: DanielVZ96
body: done
created_at: 2025-05-13T22:56:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -829,6 +830,82 @@ describe('<CourseUnit />', () => {
     });
   });
 
+  it('handles creating Video xblock and showing editor modal using videogalleryflow', async () => {
+    const waffleSpy = jest.spyOn(selectors, 'getWaffleFlags').mockReturnValue({ useVideoGalleryFlow: true });
+
+    axiosMock
+      .onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
+      .reply(200, courseCreateXblockMock);
+    const { getByText, queryByRole, getByRole } = render(<RootWrapper />);
+
+    await waitFor(() => {
+      userEvent.click(getByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage }));
+    });
+
+    axiosMock
+      .onPost(getXBlockBaseApiUrl(blockId), {
+        publish: PUBLISH_TYPES.makePublic,
+      })
+      .reply(200, { dummy: 'value' });
+    axiosMock
+      .onGet(getCourseUnitApiUrl(blockId))
+      .reply(200, {
+        ...courseUnitIndexMock,
+        visibility_state: UNIT_VISIBILITY_STATES.live,
+        has_changes: false,
+        published_by: userName,
+      });
+
+    await executeThunk(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.makePublic, true), store.dispatch);
+
+    await waitFor(() => {
+      // check if the sidebar status is Published and Live
+      expect(getByText(sidebarMessages.sidebarTitlePublishedAndLive.defaultMessage)).toBeInTheDocument();
+      expect(getByText(
+        sidebarMessages.publishLastPublished.defaultMessage
+          .replace('{publishedOn}', courseUnitIndexMock.published_on)
+          .replace('{publishedBy}', userName),
+      )).toBeInTheDocument();
+      expect(queryByRole('button', { name: sidebarMessages.actionButtonPublishTitle.defaultMessage })).not.toBeInTheDocument();
path: src/course-unit/CourseUnit.test.jsx
user: login: DanielVZ96
body: done
created_at: 2025-05-13T22:56:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: openedx-webhooks
created_at: 2025-05-01T02:30:42Z
body: Thanks for the pull request, @DanielVZ96!


This repository is currently maintained by `@bradenmacdonald`.


Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

<details><summary>:radio_button: Get product approval</summary>

If you haven't already, [check this list](https://openedx.atlassian.net/wiki/spaces/COMM/pages/3875962884/How+to+submit+an+open+source+contribution+for+Product+Review#Does-my-contribution-require-Product-Review%3F) to see if your contribution needs to go through the product review process.

- If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the [Product Working Group](https://openedx.atlassian.net/wiki/spaces/COMM/pages/3449028609/Product+Working+Group).
    - This process (including the steps you'll need to take) is documented [here](https://openedx.atlassian.net/wiki/spaces/COMM/pages/3875962884/How+to+submit+an+open+source+contribution+for+Product+Review#Product-Review-Process).
- If it doesn't, simply proceed with the next step.
</details>

<details><summary>:radio_button: Provide context</summary>

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

- Dependencies
  > This PR must be merged before / after / at the same time as ...
- Blockers
  > This PR is waiting for OEP-1234 to be accepted.
- Timeline information
  > This PR must be merged by XX date **because** ...
- Partner information
  > This is for a course on edx.org.
- Supporting documentation
- Relevant [Open edX discussion forum](https://discuss.openedx.org/) threads
</details>





<details><summary>:radio_button: Get a green build</summary>

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.
</details><details>


---

<details><summary>Where can I find more information?</summary>

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

- [Overview of Review Process for Community Contributions](https://docs.openedx.org/en/latest/developers/references/developer_guide/process/FAQ-about-pull-requests.html)
- [Pull Request Status Guide](https://docs.openedx.org/en/latest/developers/references/developer_guide/process/pull-request-statuses.html)
- [Making changes to your pull request](https://docs.openedx.org/en/latest/documentors/how-tos/make_changes_to_your_pull_request.html)
</details>

<details><summary>When can I expect my changes to be merged?</summary>

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

- The size and impact of the changes that it introduces
- The need for product review
- Maintenance status of the parent repository

:bulb: *As a result it may take up to several weeks or months to complete a review and merge your PR.*
</details>
<!-- comment:external_pr -->
<!-- data: eyJkcmFmdCI6IGZhbHNlfQ== -->

author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-01T02:56:01Z
body: ## [Codecov](https://app.codecov.io/gh/openedx/frontend-app-authoring/pull/1887?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx) Report
Attention: Patch coverage is `98.07692%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 93.80%. Comparing base [(`65aca04`)](https://app.codecov.io/gh/openedx/frontend-app-authoring/commit/65aca04708b133879881bb1f176d1509d029d918?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx) to head [(`f16834b`)](https://app.codecov.io/gh/openedx/frontend-app-authoring/commit/f16834b6c3bb0f70388756b7922f8ee3211bd3e7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx).
> Report is 37 commits behind head on master.

| [Files with missing lines](https://app.codecov.io/gh/openedx/frontend-app-authoring/pull/1887?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx) | Patch % | Lines |
|---|---|---|
| [src/course-unit/xblock-container-iframe/index.tsx](https://app.codecov.io/gh/openedx/frontend-app-authoring/pull/1887?src=pr&el=tree&filepath=src%2Fcourse-unit%2Fxblock-container-iframe%2Findex.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx#diff-c3JjL2NvdXJzZS11bml0L3hibG9jay1jb250YWluZXItaWZyYW1lL2luZGV4LnRzeA==) | 90.00% | [1 Missing :warning: ](https://app.codecov.io/gh/openedx/frontend-app-authoring/pull/1887?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #1887      +/-   ##
==========================================
+ Coverage   93.77%   93.80%   +0.02%     
==========================================
  Files        1146     1154       +8     
  Lines       23898    24106     +208     
  Branches     5042     5104      +62     
==========================================
+ Hits        22411    22612     +201     
- Misses       1419     1426       +7     
  Partials       68       68              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/openedx/frontend-app-authoring/pull/1887?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openedx).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: DanielVZ96
created_at: 2025-05-02T23:40:49Z
body: moving this to draft as we may need some changes to support video gallery based on https://github.com/openedx/edx-platform/pull/36641#discussion_r2071011681
author_association: CONTRIBUTOR
user: login: pomegranited
created_at: 2025-05-06T07:04:55Z
body: @DanielVZ96 It's ok to fix just the block duplication issues here, and use a separate PR for the video gallery fixes.
I'm seeing some weird stuff when I try to duplicate even a Text block -- when editing the new block, and after saving changes, it's like the whole Authoring MFE is iframed inside itself.

https://github.com/user-attachments/assets/56853a10-6639-4e59-a826-1eea60c1e3b5
author_association: CONTRIBUTOR