From 8ea0401c0ab22483b91b49ff08a5c9ae7e1583c9 Mon Sep 17 00:00:00 2001 From: Sawjan Gurung Date: Fri, 21 Oct 2022 23:07:15 +0545 Subject: [PATCH] fix(client): preserve scroll position of preview iframe (#47870) * fix(client): preserve scroll position of preview iframe * use existing frameContext instead of getting element * do not use type guard * remove unused type, add es2022 to ts-lib * move scroll functions into a class Co-authored-by: Shaun Hamilton --- .../Challenges/components/preview.tsx | 8 ++++- .../src/templates/Challenges/utils/frame.ts | 35 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/client/src/templates/Challenges/components/preview.tsx b/client/src/templates/Challenges/components/preview.tsx index 3f2db2a58d4..3e6834fc9ee 100644 --- a/client/src/templates/Challenges/components/preview.tsx +++ b/client/src/templates/Challenges/components/preview.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { mainPreviewId } from '../utils/frame'; +import { mainPreviewId, scrollManager } from '../utils/frame'; import './preview.css'; @@ -29,6 +29,12 @@ function Preview({ setIframeStatus(disableIframe); }, [disableIframe]); + useEffect(() => { + return () => { + scrollManager.setPreviewScrollPosition(0); + }; + }, []); + const id = previewId ?? mainPreviewId; return ( diff --git a/client/src/templates/Challenges/utils/frame.ts b/client/src/templates/Challenges/utils/frame.ts index c2959336667..906df13cc46 100644 --- a/client/src/templates/Challenges/utils/frame.ts +++ b/client/src/templates/Challenges/utils/frame.ts @@ -41,6 +41,35 @@ type InitFrame = ( frameConsoleLogger?: ProxyLogger ) => (frameContext: Context) => Context; +class ScrollManager { + #previewScrollPosition = 0; + + getPreviewScrollPosition = () => { + return this.#previewScrollPosition; + }; + + setPreviewScrollPosition = (position: number) => { + this.#previewScrollPosition = position; + }; + + registerScrollEventListener = (iframe: HTMLIFrameElement) => { + iframe.contentDocument?.addEventListener('scroll', event => { + const currentTarget = event.currentTarget as Document | null; + if (currentTarget?.body.scrollTop) { + this.setPreviewScrollPosition(currentTarget?.body.scrollTop); + } + }); + }; + + restorePreviewScrollPosition = (iframe: HTMLIFrameElement) => { + if (iframe.contentDocument?.body) { + iframe.contentDocument.body.scrollTop = this.#previewScrollPosition; + } + }; +} + +export const scrollManager = new ScrollManager(); + // we use two different frames to make them all essentially pure functions // main iframe is responsible rendering the preview and is where we proxy the export const mainPreviewId = 'fcc-main-frame'; @@ -267,6 +296,12 @@ const writeContentToFrame = (frameContext: Context) => { createHeader(frameContext.element.id) + frameContext.build, frameContext.document ); + + scrollManager.registerScrollEventListener(frameContext.element); + + if (scrollManager.getPreviewScrollPosition()) { + scrollManager.restorePreviewScrollPosition(frameContext.element); + } return frameContext; };