diff --git a/src/editors/AdvancedEditor.test.tsx b/src/editors/AdvancedEditor.test.tsx
new file mode 100644
index 0000000000..fcbd4a8860
--- /dev/null
+++ b/src/editors/AdvancedEditor.test.tsx
@@ -0,0 +1,90 @@
+import { getConfig } from '@edx/frontend-platform';
+
+import {
+ render,
+ initializeMocks,
+ waitFor,
+} from '../testUtils';
+import AdvancedEditor from './AdvancedEditor';
+
+jest.mock('./containers/EditorContainer', () => ({
+ EditorModalWrapper: jest.fn(() => (
Advanced Editor Iframe
)),
+}));
+const onCloseMock = jest.fn();
+
+describe('AdvancedEditor', () => {
+ beforeEach(() => {
+ initializeMocks();
+ });
+
+ it('should call onClose when receiving "cancel-clicked" message', () => {
+ render();
+
+ const messageEvent = new MessageEvent('message', {
+ data: {
+ type: 'xblock-event',
+ eventName: 'cancel',
+ },
+ origin: getConfig().STUDIO_BASE_URL,
+ });
+
+ window.dispatchEvent(messageEvent);
+
+ expect(onCloseMock).toHaveBeenCalled();
+ });
+
+ it('should call onClose when receiving "save-clicked" message', () => {
+ render();
+
+ const messageEvent = new MessageEvent('message', {
+ data: {
+ type: 'xblock-event',
+ eventName: 'save',
+ data: {
+ state: 'end',
+ },
+ },
+ origin: getConfig().STUDIO_BASE_URL,
+ });
+
+ window.dispatchEvent(messageEvent);
+
+ expect(onCloseMock).toHaveBeenCalled();
+ });
+
+ it('should call showToast when receiving "error" message', async () => {
+ const { mockShowToast } = initializeMocks();
+
+ render();
+
+ const messageEvent = new MessageEvent('message', {
+ data: {
+ type: 'xblock-event',
+ eventName: 'error',
+ },
+ origin: getConfig().STUDIO_BASE_URL,
+ });
+
+ window.dispatchEvent(messageEvent);
+
+ await waitFor(() => {
+ expect(mockShowToast).toHaveBeenCalled();
+ });
+ });
+
+ it('should not call onClose if the message is from an invalid origin', () => {
+ render();
+
+ const messageEvent = new MessageEvent('message', {
+ data: {
+ type: 'xblock-event',
+ eventName: 'cancel',
+ },
+ origin: 'https://invalid-origin.com',
+ });
+
+ window.dispatchEvent(messageEvent);
+
+ expect(onCloseMock).not.toHaveBeenCalled();
+ });
+});
diff --git a/src/editors/AdvancedEditor.tsx b/src/editors/AdvancedEditor.tsx
new file mode 100644
index 0000000000..17e6c82b05
--- /dev/null
+++ b/src/editors/AdvancedEditor.tsx
@@ -0,0 +1,55 @@
+import React, { useEffect } from 'react';
+import { getConfig } from '@edx/frontend-platform';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import { LibraryBlock } from '../library-authoring/LibraryBlock';
+import { EditorModalWrapper } from './containers/EditorContainer';
+import { ToastContext } from '../generic/toast-context';
+import messages from './messages';
+
+interface AdvancedEditorProps {
+ usageKey: string,
+ onClose: Function | null,
+}
+
+const AdvancedEditor = ({ usageKey, onClose }: AdvancedEditorProps) => {
+ const intl = useIntl();
+ const { showToast } = React.useContext(ToastContext);
+
+ useEffect(() => {
+ const handleIframeMessage = (event) => {
+ if (event.origin !== getConfig().STUDIO_BASE_URL) {
+ return;
+ }
+
+ if (event.data.type === 'xblock-event') {
+ const { eventName, data } = event.data;
+
+ if (onClose && (eventName === 'cancel'
+ || (eventName === 'save' && data.state === 'end'))
+ ) {
+ onClose();
+ } else if (eventName === 'error') {
+ showToast(intl.formatMessage(messages.advancedEditorGenericError));
+ }
+ }
+ };
+
+ window.addEventListener('message', handleIframeMessage);
+
+ return () => {
+ window.removeEventListener('message', handleIframeMessage);
+ };
+ }, []);
+
+ return (
+ void}>
+
+
+ );
+};
+
+export default AdvancedEditor;
diff --git a/src/editors/Editor.tsx b/src/editors/Editor.tsx
index 8e52448242..33f24c9f39 100644
--- a/src/editors/Editor.tsx
+++ b/src/editors/Editor.tsx
@@ -2,14 +2,13 @@
// as its parent, so they are tested together in EditorPage.test.tsx
import React from 'react';
import { useDispatch } from 'react-redux';
-import { FormattedMessage } from '@edx/frontend-platform/i18n';
-import messages from './messages';
import * as hooks from './hooks';
import supportedEditors from './supportedEditors';
import type { EditorComponent } from './EditorComponent';
import { useEditorContext } from './EditorContext';
+import AdvancedEditor from './AdvancedEditor';
export interface Props extends EditorComponent {
blockType: string;
@@ -43,9 +42,17 @@ const Editor: React.FC = ({
const { fullScreen } = useEditorContext();
const EditorComponent = supportedEditors[blockType];
- const innerEditor = (EditorComponent !== undefined)
- ?
- : ;
+
+ if (EditorComponent === undefined && blockId) {
+ return (
+
+ );
+ }
+
+ const innerEditor = ;
if (fullScreen) {
return (
diff --git a/src/editors/EditorPage.test.tsx b/src/editors/EditorPage.test.tsx
index 4ae9817d3b..66d7ffac95 100644
--- a/src/editors/EditorPage.test.tsx
+++ b/src/editors/EditorPage.test.tsx
@@ -26,6 +26,9 @@ jest.spyOn(editorCmsApi, 'fetchByUnitId').mockImplementation(async () => ({
}],
},
}));
+jest.mock('../library-authoring/LibraryBlock', () => ({
+ LibraryBlock: jest.fn(() => (Advanced Editor Iframe
)),
+}));
const defaultPropsHtml = {
blockId: 'block-v1:Org+TS100+24+type@html+block@123456html',
@@ -79,9 +82,7 @@ describe('EditorPage', () => {
expect(modalElement.classList).not.toContain('pgn__modal-xl');
});
- test('it shows an error message if there is no corresponding editor', async () => {
- // We can edit 'html', 'problem', and 'video' blocks.
- // But if we try to edit some other type, say 'fake', we should get an error:
+ test('it shows the Advanced Editor if there is no corresponding editor', async () => {
jest.spyOn(editorCmsApi, 'fetchBlockById').mockImplementationOnce(async () => ( // eslint-disable-next-line
{ status: 200, data: { display_name: 'Fake Un-editable Block', category: 'fake', metadata: {}, data: '' } }
));
@@ -93,6 +94,6 @@ describe('EditorPage', () => {
};
render();
- expect(await screen.findByText('Error: Could Not find Editor')).toBeInTheDocument();
+ expect(await screen.findByText('Advanced Editor Iframe')).toBeInTheDocument();
});
});
diff --git a/src/editors/messages.ts b/src/editors/messages.ts
index 4b1134a270..69cf26d681 100644
--- a/src/editors/messages.ts
+++ b/src/editors/messages.ts
@@ -1,12 +1,6 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
-
- couldNotFindEditor: {
- id: 'authoring.editorpage.selecteditor.error',
- defaultMessage: 'Error: Could Not find Editor',
- description: 'Error Message Dispayed When An unsopported Editor is desired in V2',
- },
dropVideoFileHere: {
defaultMessage: 'Drag and drop video here or click to upload',
id: 'VideoUploadEditor.dropVideoFileHere',
@@ -37,6 +31,11 @@ const messages = defineMessages({
defaultMessage: 'View in Library',
description: 'Link text for opening library block in another tab.',
},
+ advancedEditorGenericError: {
+ id: 'authoring.advancedEditor.error.generic',
+ defaultMessage: 'An unexpected error occurred in the editor',
+ description: 'Generic error message shown when an error occurs in the Advanced Editor.',
+ },
});
export default messages;
diff --git a/src/library-authoring/LibraryBlock/LibraryBlock.tsx b/src/library-authoring/LibraryBlock/LibraryBlock.tsx
index 4397090edd..e635f1f8ba 100644
--- a/src/library-authoring/LibraryBlock/LibraryBlock.tsx
+++ b/src/library-authoring/LibraryBlock/LibraryBlock.tsx
@@ -10,6 +10,7 @@ interface LibraryBlockProps {
onBlockNotification?: (event: { eventType: string; [key: string]: any }) => void;
usageKey: string;
version?: VersionSpec;
+ view?: string;
}
/**
* React component that displays an XBlock in a sandboxed IFrame.
@@ -20,7 +21,12 @@ interface LibraryBlockProps {
* cannot access things like the user's cookies, nor can it make GET/POST
* requests as the user. However, it is allowed to call any XBlock handlers.
*/
-export const LibraryBlock = ({ onBlockNotification, usageKey, version }: LibraryBlockProps) => {
+export const LibraryBlock = ({
+ onBlockNotification,
+ usageKey,
+ version,
+ view,
+}: LibraryBlockProps) => {
const iframeRef = useRef(null);
const [iFrameHeight, setIFrameHeight] = useState(50);
const studioBaseUrl = getConfig().STUDIO_BASE_URL;
@@ -71,6 +77,8 @@ export const LibraryBlock = ({ onBlockNotification, usageKey, version }: Library
const queryStr = version ? `?version=${version}` : '';
+ const xblockView = view ?? 'student_view';
+
return (
> = () => {