} events A mapping of message type to callback
+ */
+export function useIFramePluginEvents(events) {
+ const receiveMessage = useCallback(({ data }) => {
+ const {
+ type,
+ payload,
+ } = data;
+ if (events[type]) {
+ events[type](payload);
+ }
+ }, [events]);
+ useEventListener('message', receiveMessage);
+}
+
+/**
+ * A hook to monitor message about changes in iframe content height
+ * @param onIframeLoaded A callback for when the frame is loaded
+ * @returns {[boolean, number]}
+ */
+export function useIFrameHeight(onIframeLoaded = null) {
+ const [iframeHeight, setIframeHeight] = useState(null);
+ const [hasLoaded, setHasLoaded] = useState(false);
+ const receiveResizeMessage = useCallback(({ height }) => {
+ setIframeHeight(height);
+ if (!hasLoaded && !iframeHeight && height > 0) {
+ setHasLoaded(true);
+ if (onIframeLoaded) {
+ onIframeLoaded();
+ }
+ }
+ }, [setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onIframeLoaded]);
+ useIFramePluginEvents({ 'plugin.resize': receiveResizeMessage });
+ return [hasLoaded, iframeHeight];
+}
diff --git a/src/generic/hooks.test.jsx b/src/generic/hooks.test.jsx
new file mode 100644
index 00000000..2009419a
--- /dev/null
+++ b/src/generic/hooks.test.jsx
@@ -0,0 +1,45 @@
+import { render, screen, waitFor } from '@testing-library/react';
+import { useEventListener, useIFrameHeight } from './hooks';
+
+describe('Hooks', () => {
+ test('useEventListener', async () => {
+ const handler = jest.fn();
+ const TestComponent = () => {
+ useEventListener('message', handler);
+ return ();
+ };
+ render();
+
+ await screen.findByTestId('testid');
+ window.postMessage({ test: 'test' }, '*');
+ await waitFor(() => expect(handler).toHaveBeenCalled());
+ });
+ test('useIFrameHeight', async () => {
+ const onLoaded = jest.fn();
+ const TestComponent = () => {
+ const [hasLoaded, height] = useIFrameHeight(onLoaded);
+ return (
+
+
+ {String(hasLoaded)}
+
+
+ {String(height)}
+
+
+ );
+ };
+ render();
+
+ await screen.findByTestId('testid');
+ expect(screen.getByTestId('loaded')).toHaveTextContent('false');
+ expect(screen.getByTestId('height')).toHaveTextContent('null');
+ window.postMessage({
+ type: 'plugin.resize',
+ payload: { height: 1234 },
+ }, '*');
+ await waitFor(() => expect(onLoaded).toHaveBeenCalled());
+ await waitFor(() => expect(screen.getByTestId('loaded')).toHaveTextContent('true'));
+ expect(screen.getByTestId('height')).toHaveTextContent('1234');
+ });
+});
diff --git a/src/index.jsx b/src/index.jsx
index eef01fb9..7db78a6c 100755
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -12,6 +12,8 @@ import { Switch } from 'react-router-dom';
import { messages as footerMessages } from '@edx/frontend-component-footer';
import { messages as headerMessages } from '@edx/frontend-component-header';
+import { fetchDiscussionTab } from './course-home/data/thunks';
+import DiscussionTab from './course-home/discussion-tab/DiscussionTab';
import appMessages from './i18n';
import { UserMessagesProvider } from './generic/user-messages';
@@ -51,6 +53,11 @@ subscribe(APP_READY, () => {
+
+
+
+
+