Skip to content

Commit 73610bf

Browse files
ihor-romaniukleangseu-edx
authored andcommitted
fix: save scroll position on exit from video xblock fullscreen mode
1 parent 1c025f0 commit 73610bf

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

src/courseware/course/sequence/Unit.jsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const Unit = ({
9393
const [showError, setShowError] = useState(false);
9494
const [modalOptions, setModalOptions] = useState({ open: false });
9595
const [shouldDisplayHonorCode, setShouldDisplayHonorCode] = useState(false);
96+
const [windowTopOffset, setWindowTopOffset] = useState(null);
9697

9798
const unit = useModel('units', id);
9899
const course = useModel('coursewareMeta', courseId);
@@ -120,6 +121,13 @@ const Unit = ({
120121
} = data;
121122
if (type === 'plugin.resize') {
122123
setIframeHeight(payload.height);
124+
125+
// We observe exit from the video xblock full screen mode
126+
// and do page scroll to the previously saved scroll position
127+
if (windowTopOffset !== null) {
128+
window.scrollTo(0, Number(windowTopOffset));
129+
}
130+
123131
if (!hasLoaded && iframeHeight === 0 && payload.height > 0) {
124132
setHasLoaded(true);
125133
if (onLoaded) {
@@ -129,12 +137,16 @@ const Unit = ({
129137
} else if (type === 'plugin.modal') {
130138
payload.open = true;
131139
setModalOptions(payload);
140+
} else if (type === 'plugin.videoFullScreen') {
141+
// We listen for this message from LMS to know when we need to
142+
// save or reset scroll position on toggle video xblock full screen mode.
143+
setWindowTopOffset(payload.open ? window.scrollY : null);
132144
} else if (data.offset) {
133145
// We listen for this message from LMS to know when the page needs to
134146
// be scrolled to another location on the page.
135147
window.scrollTo(0, data.offset + document.getElementById('unit-iframe').offsetTop);
136148
}
137-
}, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded]);
149+
}, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded, setWindowTopOffset, windowTopOffset]);
138150
useEventListener('message', receiveMessage);
139151
useEffect(() => {
140152
sendUrlHashToFrame(document.getElementById('unit-iframe'));

src/courseware/course/sequence/Unit.test.jsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,21 @@ describe('Unit', () => {
129129
expect(window.scrollY === testMessageWithOffset.offset);
130130
});
131131

132+
it('scrolls page on MessagaeEvent when receiving videoFullScreen state', async () => {
133+
// Set message to constain video full screen data.
134+
const defaultTopOffset = 800;
135+
const testMessageWithOtherHeight = { ...messageEvent, payload: { height: 500 } };
136+
const testMessageWithFullscreenState = (isOpen) => ({ type: 'plugin.videoFullScreen', payload: { open: isOpen } });
137+
render(<Unit {...mockData} />);
138+
Object.defineProperty(window, 'scrollY', { value: defaultTopOffset, writable: true });
139+
window.postMessage(testMessageWithFullscreenState(true), '*');
140+
window.postMessage(testMessageWithFullscreenState(false), '*');
141+
window.postMessage(testMessageWithOtherHeight, '*');
142+
143+
await expect(waitFor(() => expect(window.scrollTo()).toHaveBeenCalledTimes(1)));
144+
expect(window.scrollY === defaultTopOffset);
145+
});
146+
132147
it('ignores MessageEvent with unhandled type', async () => {
133148
// Clone message and set different type.
134149
const testMessageWithUnhandledType = { ...messageEvent, type: 'wrong type' };

0 commit comments

Comments
 (0)