diff --git a/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.test.js b/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.test.js index 35d59b4d60..34e56c22e4 100644 --- a/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.test.js +++ b/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.test.js @@ -280,7 +280,7 @@ describe('useIFrameBehavior hook', () => { }); }); describe('visibility tracking', () => { - it('sets up visibility tracking after iframe has loaded', () => { + it('sets up visibility tracking after iframe loads', () => { mockState({ ...defaultStateVals, hasLoaded: true }); renderHook(() => useIFrameBehavior(props)); @@ -288,15 +288,9 @@ describe('useIFrameBehavior hook', () => { expect(global.window.addEventListener).toHaveBeenCalledTimes(2); expect(global.window.addEventListener).toHaveBeenCalledWith('scroll', expect.any(Function)); expect(global.window.addEventListener).toHaveBeenCalledWith('resize', expect.any(Function)); - // Initial visibility update. - expect(postMessage).toHaveBeenCalledWith( - { - type: 'unit.visibilityStatus', - data: { - topPosition: 100, - viewportHeight: 800, - }, - }, + // Initial visibility update is handled by the `handleIFrameLoad` method. + expect(postMessage).not.toHaveBeenCalledWith( + expect.objectContaining({ type: 'unit.visibilityStatus' }), config.LMS_BASE_URL, ); }); @@ -362,6 +356,20 @@ describe('useIFrameBehavior hook', () => { window.onmessage(event); expect(dispatch).toHaveBeenCalledWith(processEvent(event.data, fetchCourse)); }); + it('updates initial iframe visibility on load', () => { + const { result } = renderHook(() => useIFrameBehavior(props)); + result.current.handleIFrameLoad(); + expect(postMessage).toHaveBeenCalledWith( + { + type: 'unit.visibilityStatus', + data: { + topPosition: 100, + viewportHeight: 800, + }, + }, + config.LMS_BASE_URL, + ); + }); }); it('forwards handleIframeLoad, showError, and hasLoaded from state fields', () => { mockState(stateVals); diff --git a/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.ts b/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.ts index 9e63015b93..4a882da321 100644 --- a/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.ts +++ b/src/courseware/course/sequence/Unit/hooks/useIFrameBehavior.ts @@ -102,6 +102,23 @@ const useIFrameBehavior = ({ useEventListener('message', receiveMessage); // Send visibility status to the iframe. It's used to mark XBlocks as viewed. + const updateIframeVisibility = () => { + const iframeElement = document.getElementById(elementId) as HTMLIFrameElement | null; + const rect = iframeElement?.getBoundingClientRect(); + const visibleInfo = { + type: 'unit.visibilityStatus', + data: { + topPosition: rect?.top, + viewportHeight: window.innerHeight, + }, + }; + iframeElement?.contentWindow?.postMessage( + visibleInfo, + `${getConfig().LMS_BASE_URL}`, + ); + }; + + // Set up visibility tracking event listeners. React.useEffect(() => { if (!hasLoaded) { return undefined; @@ -112,27 +129,9 @@ const useIFrameBehavior = ({ return undefined; } - const updateIframeVisibility = () => { - const rect = iframeElement.getBoundingClientRect(); - const visibleInfo = { - type: 'unit.visibilityStatus', - data: { - topPosition: rect.top, - viewportHeight: window.innerHeight, - }, - }; - iframeElement?.contentWindow?.postMessage( - visibleInfo, - `${getConfig().LMS_BASE_URL}`, - ); - }; - // Throttle the update function to prevent it from sending too many messages to the iframe. const throttledUpdateVisibility = throttle(updateIframeVisibility, 100); - // Update the visibility of the iframe in case the element is already visible. - updateIframeVisibility(); - // Add event listeners to update the visibility of the iframe when the window is scrolled or resized. window.addEventListener('scroll', throttledUpdateVisibility); window.addEventListener('resize', throttledUpdateVisibility); @@ -167,6 +166,9 @@ const useIFrameBehavior = ({ dispatch(processEvent(e.data, fetchCourse)); } }; + + // Update the visibility of the iframe in case the element is already visible. + updateIframeVisibility(); }; React.useEffect(() => {