From a52363132d0bfae8de2183933a1b1dbf27ac1d66 Mon Sep 17 00:00:00 2001 From: ihor-romaniuk Date: Fri, 3 Mar 2023 12:10:42 +0200 Subject: [PATCH 1/2] fix: save scroll position on exit from video xblock fullscreen mode --- src/courseware/course/sequence/Unit.jsx | 14 +++++++++++++- src/courseware/course/sequence/Unit.test.jsx | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/courseware/course/sequence/Unit.jsx b/src/courseware/course/sequence/Unit.jsx index 31a9bf9343..af4f85aa12 100644 --- a/src/courseware/course/sequence/Unit.jsx +++ b/src/courseware/course/sequence/Unit.jsx @@ -96,6 +96,7 @@ function Unit({ const [showError, setShowError] = useState(false); const [modalOptions, setModalOptions] = useState({ open: false }); const [shouldDisplayHonorCode, setShouldDisplayHonorCode] = useState(false); + const [windowTopOffset, setWindowTopOffset] = useState(null); const unit = useModel('units', id); const course = useModel('coursewareMeta', courseId); @@ -123,6 +124,13 @@ function 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) { @@ -132,12 +140,16 @@ function 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 402c620a88..2a78a549bc 100644 --- a/src/courseware/course/sequence/Unit.test.jsx +++ b/src/courseware/course/sequence/Unit.test.jsx @@ -131,6 +131,21 @@ describe('Unit', () => { expect(window.scrollY === testMessageWithOffset.offset); }); + it('scrolls page on MessageEvent when receiving videoFullScreen state', async () => { + // Set message to contain video full screen data. + const defaultTopOffset = 800; + const testMessageWithOtherHeight = { ...messageEvent, payload: { height: 500 } }; + const testMessageWithFullscreenState = (isShow) => ({ type: 'plugin.videoFullScreen', payload: { show: isShow } }); + 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' }; From 7b37fb90edab51b18beab65e1033baa1c459a38a Mon Sep 17 00:00:00 2001 From: ihor-romaniuk Date: Fri, 5 May 2023 11:57:13 +0300 Subject: [PATCH 2/2] fix: update tests --- src/courseware/course/sequence/Unit.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/courseware/course/sequence/Unit.test.jsx b/src/courseware/course/sequence/Unit.test.jsx index 2a78a549bc..e7f7e2ad6e 100644 --- a/src/courseware/course/sequence/Unit.test.jsx +++ b/src/courseware/course/sequence/Unit.test.jsx @@ -135,7 +135,7 @@ describe('Unit', () => { // Set message to contain video full screen data. const defaultTopOffset = 800; const testMessageWithOtherHeight = { ...messageEvent, payload: { height: 500 } }; - const testMessageWithFullscreenState = (isShow) => ({ type: 'plugin.videoFullScreen', payload: { show: isShow } }); + const testMessageWithFullscreenState = (isOpen) => ({ type: 'plugin.videoFullScreen', payload: { open: isOpen } }); render(); Object.defineProperty(window, 'scrollY', { value: defaultTopOffset, writable: true }); window.postMessage(testMessageWithFullscreenState(true), '*');