diff --git a/src/courseware/course/sequence/Unit.jsx b/src/courseware/course/sequence/Unit.jsx
index 1e80ff70..2c11e000 100644
--- a/src/courseware/course/sequence/Unit.jsx
+++ b/src/courseware/course/sequence/Unit.jsx
@@ -70,6 +70,15 @@ function useLoadBearingHook(id) {
}, [id]);
}
+export function sendUrlHashToFrame(frame) {
+ const { hash } = window.location;
+ if (hash) {
+ // The url hash will be sent to LMS-served iframe in order to find the location of the
+ // hash within the iframe.
+ frame.contentWindow.postMessage({ hashName: hash }, `${getConfig().LMS_BASE_URL}`);
+ }
+}
+
function Unit({
courseId,
format,
@@ -98,13 +107,13 @@ function Unit({
} = course;
const dispatch = useDispatch();
-
// Do not remove this hook. See function description.
useLoadBearingHook(id);
// We use this ref so that we can hold a reference to the currently active event listener.
const messageEventListenerRef = useRef(null);
useEffect(() => {
+ sendUrlHashToFrame(document.getElementById('unit-iframe'));
function receiveMessage(event) {
const { type, payload } = event.data;
if (type === 'plugin.resize') {
@@ -120,7 +129,7 @@ function Unit({
setModalOptions(payload);
} else if (event.data.offset) {
// We listen for this message from LMS to know when the page needs to
- // be scroll to another location on the page.
+ // be scrolled to another location on the page.
window.scrollTo(0, event.data.offset);
}
}
diff --git a/src/courseware/course/sequence/Unit.test.jsx b/src/courseware/course/sequence/Unit.test.jsx
index 02ba79a7..e3ee3149 100644
--- a/src/courseware/course/sequence/Unit.test.jsx
+++ b/src/courseware/course/sequence/Unit.test.jsx
@@ -3,7 +3,7 @@ import { Factory } from 'rosie';
import {
initializeTestStore, loadUnit, messageEvent, render, screen, waitFor,
} from '../../../setupTest';
-import Unit from './Unit';
+import Unit, { sendUrlHashToFrame } from './Unit';
describe('Unit', () => {
let mockData;
@@ -38,7 +38,6 @@ describe('Unit', () => {
it('renders correctly', () => {
render();
-
expect(screen.getByText('Loading learning sequence...')).toBeInTheDocument();
const renderedUnit = screen.getByTitle(unit.display_name);
expect(renderedUnit).toHaveAttribute('height', String(0));
@@ -49,7 +48,6 @@ describe('Unit', () => {
it('renders proper message for gated content', () => {
render();
-
expect(screen.getByText('Loading learning sequence...')).toBeInTheDocument();
expect(screen.getByText('Loading locked content messaging...')).toBeInTheDocument();
});
@@ -75,7 +73,6 @@ describe('Unit', () => {
it('handles receiving MessageEvent', async () => {
render();
loadUnit();
-
// Loading message is gone now.
await waitFor(() => expect(screen.queryByText('Loading learning sequence...')).not.toBeInTheDocument());
// Iframe's height is set via message.
@@ -125,4 +122,37 @@ describe('Unit', () => {
{ timeout: 100 },
)).rejects.toThrowError(/Expected the element to have attribute/);
});
+
+ it('scrolls to correct place onLoad', () => {
+ document.body.innerHTML = "";
+
+ const mockHashCheck = jest.fn(frameVar => sendUrlHashToFrame(frameVar));
+ const frame = document.getElementById('unit-iframe');
+ const originalWindow = { ...window };
+ const windowSpy = jest.spyOn(global, 'window', 'get');
+ windowSpy.mockImplementation(() => ({
+ ...originalWindow,
+ location: {
+ ...originalWindow.location,
+ hash: '#test',
+ },
+ }));
+ const messageSpy = jest.spyOn(frame.contentWindow, 'postMessage');
+ messageSpy.mockImplementation(() => ({ hashName: originalWindow.location.hash }));
+ mockHashCheck(frame);
+
+ expect(mockHashCheck).toHaveBeenCalled();
+ expect(messageSpy).toHaveBeenCalled();
+
+ windowSpy.mockRestore();
+ });
+
+ it('calls useEffect and checkForHash', () => {
+ const mockHashCheck = jest.fn(() => sendUrlHashToFrame());
+ const effectSpy = jest.spyOn(React, 'useEffect');
+ effectSpy.mockImplementation(() => mockHashCheck());
+ render();
+ expect(React.useEffect).toHaveBeenCalled();
+ expect(mockHashCheck).toHaveBeenCalled();
+ });
});