diff --git a/src/course-unit/CourseUnit.jsx b/src/course-unit/CourseUnit.jsx index 67088658d2..a01d9817f7 100644 --- a/src/course-unit/CourseUnit.jsx +++ b/src/course-unit/CourseUnit.jsx @@ -53,7 +53,6 @@ const CourseUnit = ({ courseId }) => { handleXBlockDragAndDrop, canPasteComponent, } = useCourseUnit({ courseId, blockId }); - const initialXBlocksData = useMemo(() => courseVerticalChildren.children ?? [], [courseVerticalChildren.children]); const [unitXBlocks, setUnitXBlocks] = useState(initialXBlocksData); diff --git a/src/course-unit/course-xblock/CourseXBlock.jsx b/src/course-unit/course-xblock/CourseXBlock.jsx index f7734522bc..94bb4615e4 100644 --- a/src/course-unit/course-xblock/CourseXBlock.jsx +++ b/src/course-unit/course-xblock/CourseXBlock.jsx @@ -21,6 +21,7 @@ import { } from '../data/selectors'; import { copyToClipboard, + fetchXBlockModalDataQuery, fetchXBlockIframeHtmlAndResourcesQuery, } from '../data/thunk'; import { COMPONENT_TYPES } from '../constants'; @@ -44,6 +45,7 @@ const CourseXBlock = ({ const intl = useIntl(); const xblockIframeHtmlAndResources = useSelector(getXBlockIframeHtmlAndResources); const xblockInstanceHtmlAndResources = find(xblockIframeHtmlAndResources, { xblockId: id }); + const xblockModalData = useSelector(state => state.courseUnit.xblockModalData); const visibilityMessage = userPartitionInfo.selectedGroupsLabel ? intl.formatMessage(messages.visibilityMessage, { selectedGroupsLabel: userPartitionInfo.selectedGroupsLabel }) @@ -73,6 +75,7 @@ const CourseXBlock = ({ navigate(`/course/${courseId}/editor/${type}/${id}`); break; default: + dispatch(fetchXBlockModalDataQuery(id)); } }; @@ -88,83 +91,86 @@ const CourseXBlock = ({ }, []); return ( -
- - - - - + +
+ + + - - unitXBlockActions.handleDuplicate(id)}> - {intl.formatMessage(messages.blockLabelButtonDuplicate)} - - - {intl.formatMessage(messages.blockLabelButtonMove)} - - {canEdit && ( - dispatch(copyToClipboard(id))}> - {intl.formatMessage(messages.blockLabelButtonCopyToClipboard)} + + + + unitXBlockActions.handleDuplicate(id)}> + {intl.formatMessage(messages.blockLabelButtonDuplicate)} - )} - - {intl.formatMessage(messages.blockLabelButtonManageAccess)} - - - {intl.formatMessage(messages.blockLabelButtonDelete)} - - - - - - - )} - /> - - {renderError ? : ( - <> - - {xblockInstanceHtmlAndResources && ( - + {intl.formatMessage(messages.blockLabelButtonMove)} + + {canEdit && ( + dispatch(copyToClipboard(id))}> + {intl.formatMessage(messages.blockLabelButtonCopyToClipboard)} + + )} + + {intl.formatMessage(messages.blockLabelButtonManageAccess)} + + + {intl.formatMessage(messages.blockLabelButtonDelete)} + + + + + - )} - - )} - - -
+ + )} + /> + + {renderError ? : ( + <> + + {xblockInstanceHtmlAndResources && ( + + )} + + )} + +
+
+ ); }; diff --git a/src/course-unit/course-xblock/CourseXblock.scss b/src/course-unit/course-xblock/CourseXblock.scss index 6e5c4b516b..22e9bc4e34 100644 --- a/src/course-unit/course-xblock/CourseXblock.scss +++ b/src/course-unit/course-xblock/CourseXblock.scss @@ -36,3 +36,12 @@ margin-bottom: 0; } } + +.xblock-iframe-modal-wrapper { + height: 100%; + position: absolute; + z-index: 99999999999; + top: 0; + left: 0; + width: 100%; +} diff --git a/src/course-unit/course-xblock/xblock-content/XBlockContent.jsx b/src/course-unit/course-xblock/xblock-content/XBlockContent.jsx index 1e6a620878..3741c859bc 100644 --- a/src/course-unit/course-xblock/xblock-content/XBlockContent.jsx +++ b/src/course-unit/course-xblock/xblock-content/XBlockContent.jsx @@ -19,7 +19,7 @@ ensureConfig(['STUDIO_BASE_URL', 'SECURE_ORIGIN_XBLOCK_BOOTSTRAP_HTML_URL'], 'st * requests as the user. However, it is allowed to call any XBlock handlers. */ const XBlockContent = ({ - view, type, getHandlerUrl, onBlockNotification, + view, type, getHandlerUrl, onBlockNotification, variant, }) => { const iframeRef = useRef(null); const [html, setHtml] = useState(null); @@ -35,6 +35,7 @@ const XBlockContent = ({ view.resources, getConfig().STUDIO_BASE_URL, type, + variant, ); // Load the XBlock HTML into the IFrame: diff --git a/src/course-unit/course-xblock/xblock-content/iframe-wrapper/iframe-wrapper.js b/src/course-unit/course-xblock/xblock-content/iframe-wrapper/iframe-wrapper.js index 3eb53cd4f4..f20489b719 100644 --- a/src/course-unit/course-xblock/xblock-content/iframe-wrapper/iframe-wrapper.js +++ b/src/course-unit/course-xblock/xblock-content/iframe-wrapper/iframe-wrapper.js @@ -18,7 +18,7 @@ import { * JS and CSS dependencies properly. * @param type The XBlock's type (openassessment, discussion, video, etc.) */ -export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBaseUrl, type) { +export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBaseUrl, type, variant) { const resources = normalizeResources(sourceResources); /* Extract CSS resources. */ @@ -36,7 +36,7 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase jsTags += scripts.map(script => ``).join('\n'); let legacyIncludes = ''; - if (html.indexOf('wrapper-xblock-message') !== -1) { + if (html.indexOf('wrapper-xblock-message') !== -1 || html.indexOf('xmodule_edit') !== -1) { legacyIncludes += ` @@ -251,6 +251,53 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase modifiedHtml = modifiedHtml.replace('src="/static/studio', `src="${studioBaseUrl}/static/studio`); modifiedHtml = modifiedHtml.replace('data-target="/preview/xblock', `data-target="${studioBaseUrl}/preview/xblock`); + const hasCustomButtons = html.includes('editor-with-buttons'); + const hasCustomTabs = html.includes('editor-with-tabs'); + const hasPlugins = html.includes('wrapper-comp-plugins'); + console.log('TYPE:', type); + console.log('VARIANT:', variant); + const getDataEditor = html.includes('wrapper-comp-editor'); + + const editingModalHeader = ` + `; + + const defaultModalTitle = ` + `; + + const getActionBar = ` + `; + if ( type === COMPONENT_TYPES.discussion || type === COMPONENT_TYPES.dragAndDrop @@ -328,5 +375,41 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase `; } + if (variant === 'edit-modal') { + result = ` + + + + + + + ${legacyIncludes} + ${cssTags} + + + +
+ + +
+ ${jsTags} + + + + `; + } + return result; } diff --git a/src/course-unit/data/api.js b/src/course-unit/data/api.js index 80c59dab42..afce81a683 100644 --- a/src/course-unit/data/api.js +++ b/src/course-unit/data/api.js @@ -13,6 +13,7 @@ export const getCourseSectionVerticalApiUrl = (itemId) => `${getStudioBaseUrl()} export const getCourseVerticalChildrenApiUrl = (itemId) => `${getStudioBaseUrl()}/api/contentstore/v1/container/vertical/${itemId}/children`; export const getClipboardUrl = () => `${getStudioBaseUrl()}/api/content-staging/v1/clipboard/`; export const getXBlockContainerPreview = (itemId) => `${getStudioBaseUrl()}/xblock/${itemId}/container_preview`; +export const getXBlockModalDataApiUrl = (itemId) => `${getStudioBaseUrl()}/xblock/${itemId}/studio_view`; export const postXBlockBaseApiUrl = () => `${getStudioBaseUrl()}/xblock/`; @@ -199,3 +200,10 @@ export async function getXBlockIframeData(itemId) { return camelCaseObject(data); } + +export async function getXBlockModalData(itemId) { + const { data } = await getAuthenticatedHttpClient() + .get(getXBlockModalDataApiUrl(itemId)); + + return camelCaseObject(data); +} diff --git a/src/course-unit/data/slice.js b/src/course-unit/data/slice.js index a538b4f0d3..7e7e673c4f 100644 --- a/src/course-unit/data/slice.js +++ b/src/course-unit/data/slice.js @@ -22,6 +22,7 @@ const slice = createSlice({ clipboardData: null, staticFileNotices: {}, xblockIframeHtmlAndResources: [], + xblockModalData: [], }, reducers: { fetchCourseItemSuccess: (state, { payload }) => { @@ -119,6 +120,9 @@ const slice = createSlice({ fetchXBlockIframeResources: (state, { payload }) => { state.xblockIframeHtmlAndResources.push(payload); }, + fetchXBlockModalData: (state, { payload }) => { + state.xblockModalData = payload; + }, }, }); @@ -143,6 +147,7 @@ export const { fetchStaticFileNoticesSuccess, reorderXBlockList, fetchXBlockIframeResources, + fetchXBlockModalData, } = slice.actions; export const { diff --git a/src/course-unit/data/thunk.js b/src/course-unit/data/thunk.js index 285d4e115d..c65f3bcca5 100644 --- a/src/course-unit/data/thunk.js +++ b/src/course-unit/data/thunk.js @@ -23,6 +23,7 @@ import { duplicateUnitItem, setXBlockOrderList, getXBlockIframeData, + getXBlockModalData, } from './api'; import { updateLoadingCourseUnitStatus, @@ -42,7 +43,7 @@ import { duplicateXBlock, fetchStaticFileNoticesSuccess, reorderXBlockList, - fetchXBlockIframeResources, + fetchXBlockIframeResources, fetchXBlockModalData, } from './slice'; import { getNotificationMessage } from './utils'; @@ -322,3 +323,20 @@ export function fetchXBlockIframeHtmlAndResourcesQuery(xblockId) { } }; } + +export function fetchXBlockModalDataQuery(xblockId) { + return async (dispatch) => { + dispatch(updateSavingStatus({ status: RequestStatus.PENDING })); + dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.adding)); + + try { + const xblockModalData = await getXBlockModalData(xblockId); + dispatch(fetchXBlockModalData({ xblockId, ...xblockModalData })); + dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL })); + } catch (error) { + dispatch(updateSavingStatus({ status: RequestStatus.FAILED })); + } finally { + dispatch(hideProcessingNotification()); + } + }; +}