diff --git a/src/courseware/course/sequence/Unit.jsx b/src/courseware/course/sequence/Unit.jsx index 05f44ccfc3..828ce08f78 100644 --- a/src/courseware/course/sequence/Unit.jsx +++ b/src/courseware/course/sequence/Unit.jsx @@ -108,6 +108,7 @@ const Unit = ({ const [shouldDisplayHonorCode, setShouldDisplayHonorCode] = useState(false); const [examAccessToken, setExamAccessToken] = useState(''); const [blockExamAccess, setBlockExamAccess] = useState(isExam()); + const [windowTopOffset, setWindowTopOffset] = useState(null); const unit = useModel('units', id); const course = useModel('coursewareMeta', courseId); @@ -135,6 +136,13 @@ const Unit = ({ } = data; if (type === 'plugin.resize') { setIframeHeight(payload.height); + + // We observe exit from the video xblock full screen mode + // and do page scroll to the previously saved scroll position + if (windowTopOffset !== null) { + window.scrollTo(0, Number(windowTopOffset)); + } + if (!hasLoaded && iframeHeight === 0 && payload.height > 0) { setHasLoaded(true); if (onLoaded) { @@ -144,12 +152,16 @@ const Unit = ({ } else if (type === 'plugin.modal') { payload.open = true; setModalOptions(payload); + } else if (type === 'plugin.videoFullScreen') { + // We listen for this message from LMS to know when we need to + // save or reset scroll position on toggle video xblock full screen mode. + setWindowTopOffset(payload.open ? window.scrollY : null); } else if (data.offset) { // We listen for this message from LMS to know when the page needs to // be scrolled to another location on the page. window.scrollTo(0, data.offset + document.getElementById('unit-iframe').offsetTop); } - }, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded]); + }, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded, setWindowTopOffset, windowTopOffset]); useEventListener('message', receiveMessage); useEffect(() => { sendUrlHashToFrame(document.getElementById('unit-iframe')); diff --git a/src/courseware/course/sequence/Unit.test.jsx b/src/courseware/course/sequence/Unit.test.jsx index 9e3a8eaef4..4416b5f186 100644 --- a/src/courseware/course/sequence/Unit.test.jsx +++ b/src/courseware/course/sequence/Unit.test.jsx @@ -144,6 +144,21 @@ describe('Unit', () => { expect(window.scrollY === testMessageWithOffset.offset); }); + it('scrolls page on MessagaeEvent when receiving videoFullScreen state', async () => { + // Set message to constain video full screen data. + const defaultTopOffset = 800; + const testMessageWithOtherHeight = { ...messageEvent, payload: { height: 500 } }; + const testMessageWithFullscreenState = (isOpen) => ({ type: 'plugin.videoFullScreen', payload: { open: isOpen } }); + render(); + Object.defineProperty(window, 'scrollY', { value: defaultTopOffset, writable: true }); + window.postMessage(testMessageWithFullscreenState(true), '*'); + window.postMessage(testMessageWithFullscreenState(false), '*'); + window.postMessage(testMessageWithOtherHeight, '*'); + + await expect(waitFor(() => expect(window.scrollTo()).toHaveBeenCalledTimes(1))); + expect(window.scrollY === defaultTopOffset); + }); + it('ignores MessageEvent with unhandled type', async () => { // Clone message and set different type. const testMessageWithUnhandledType = { ...messageEvent, type: 'wrong type' };