From 7c3ca821ebc121cf27128a49c22f920de8fafeec Mon Sep 17 00:00:00 2001 From: Peter Kulko Date: Tue, 5 Nov 2024 17:25:37 +0200 Subject: [PATCH] refactor: edit modal functional --- src/course-unit/CourseUnit.scss | 16 ---- src/course-unit/constants.js | 4 +- .../xblock-container-iframe/index.tsx | 79 ++++++++----------- src/generic/modal-iframe/ModalIframe.scss | 13 +++ src/generic/modal-iframe/ModalIframe.tsx | 44 +++++++++++ src/generic/styles.scss | 1 + 6 files changed, 92 insertions(+), 65 deletions(-) create mode 100644 src/generic/modal-iframe/ModalIframe.scss create mode 100644 src/generic/modal-iframe/ModalIframe.tsx diff --git a/src/course-unit/CourseUnit.scss b/src/course-unit/CourseUnit.scss index 67c0165271..3ada01ca2f 100644 --- a/src/course-unit/CourseUnit.scss +++ b/src/course-unit/CourseUnit.scss @@ -8,19 +8,3 @@ .course-unit__alert { margin-bottom: 1.75rem; } - -.modal-window-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: #000; - opacity: 0.5; - z-index: 1000; -} - -iframe { - z-index: 99999999999999; - position: relative; -} \ No newline at end of file diff --git a/src/course-unit/constants.js b/src/course-unit/constants.js index f16303a9ab..3098d4a0dd 100644 --- a/src/course-unit/constants.js +++ b/src/course-unit/constants.js @@ -56,10 +56,12 @@ export const messageTypes = { manageXBlockAccess: 'manageXBlockAccess', deleteXBlock: 'deleteXBlock', duplicateXBlock: 'duplicateXBlock', - refreshPositions: 'refreshPositions', newXBlockEditor: 'newXBlockEditor', showXBlockEditorModal: 'showXBlockEditorModal', hideXBlockEditorModal: 'hideXBlockEditorModal', + editXBlock: 'editXBlock', + closeXBlockEditorModal: 'closeXBlockEditorModal', + saveEditedXBlockData: 'saveEditedXBlockData', }; export const IFRAME_FEATURE_POLICY = ( diff --git a/src/course-unit/xblock-container-iframe/index.tsx b/src/course-unit/xblock-container-iframe/index.tsx index 9e36053ed6..a04400b332 100644 --- a/src/course-unit/xblock-container-iframe/index.tsx +++ b/src/course-unit/xblock-container-iframe/index.tsx @@ -9,10 +9,10 @@ import { useNavigate } from 'react-router-dom'; import DeleteModal from '../../generic/delete-modal/DeleteModal'; import ConfigureModal from '../../generic/configure-modal/ConfigureModal'; +import ModalIframe from '../../generic/modal-iframe/ModalIframe'; import { copyToClipboard } from '../../generic/data/thunks'; import { COURSE_BLOCK_NAMES } from '../../constants'; import { IFRAME_FEATURE_POLICY, messageTypes } from '../constants'; -import { fetchCourseUnitQuery } from '../data/thunk'; import { useIframe } from '../context/hooks'; import { useIFrameBehavior } from './hooks'; import messages from './messages'; @@ -76,21 +76,15 @@ const XBlockContainerIframe: FC = ({ const { setIframeRef, sendMessageToIframe } = useIframe(); const [editXblockId, setEditXblockId] = useState(null); const [currentXblockData, setCurrentXblockData] = useState({}); - const [showOverlay, setShowOverlay] = useState(false); - const [scrollPosition, setScrollPosition] = useState(0); + const [showLegacyEditModal, setShowLegacyEditModal] = useState(false); const iframeUrl = `${getConfig().STUDIO_BASE_URL}/container_embed/${blockId}`; + const editXBlockModalUrl = `${getConfig().STUDIO_BASE_URL}/xblock/${editXblockId}/actions/edit`; useEffect(() => { setIframeRef(iframeRef); }, [setIframeRef]); - useEffect(() => { - sendMessageToIframe('controlEditModalPosition', { - height: scrollPosition, - }); - }, [scrollPosition]); - const handleDelete = (id: string) => { openDeleteModal(); setDeleteXblockId(id); @@ -114,60 +108,54 @@ const XBlockContainerIframe: FC = ({ } }; - document.addEventListener('scroll', () => { - setScrollPosition(window.scrollY - 200); - }); - const handleCopy = (id: string) => { dispatch(copyToClipboard(id)); }; - const handleDuplicateXBlock = (id) => { - if (id) { - unitXBlockActions.handleDuplicate(id); - // TODO: this artificial delay is a temporary solution - // to ensure the iframe content is properly refreshed. - setTimeout(() => { - sendMessageToIframe(messageTypes.refreshXBlock, null); - }, 1000); - } - }; - const handleRefreshXBlocks = () => { // TODO: this artificial delay is a temporary solution // to ensure the iframe content is properly refreshed. setTimeout(() => { - dispatch(fetchCourseUnitQuery(blockId)); + sendMessageToIframe(messageTypes.refreshXBlock, null); }, 1000); }; + const handleDuplicateXBlock = (id) => { + if (id) { + unitXBlockActions.handleDuplicate(id); + handleRefreshXBlocks(); + } + }; + const navigateToNewXBlockEditor = (url: string) => { navigate(`/course/${courseId}/editor${url}`); }; + const handleShowEditXBlockModal = (id: string) => { + setEditXblockId(id); + setShowLegacyEditModal(true); + }; + + const handleCloseEditorXBlockModal = () => { + setEditXblockId(null); + setShowLegacyEditModal(false); + }; + useEffect(() => { const messageHandlers: Record void> = { [messageTypes.deleteXBlock]: (payload) => handleDelete(payload.id), [messageTypes.manageXBlockAccess]: (payload) => handleConfigure(payload.id), [messageTypes.copyXBlock]: (payload) => handleCopy(payload.id), [messageTypes.duplicateXBlock]: (payload) => handleDuplicateXBlock(payload.id), - [messageTypes.refreshPositions]: handleRefreshXBlocks, [messageTypes.newXBlockEditor]: (payload) => navigateToNewXBlockEditor(payload.url), + [messageTypes.editXBlock]: (payload) => handleShowEditXBlockModal(payload.id), + [messageTypes.closeXBlockEditorModal]: () => handleCloseEditorXBlockModal(), + [messageTypes.saveEditedXBlockData]: handleRefreshXBlocks, }; const handleMessage = (event: MessageEvent) => { const { type, payload } = event.data || {}; - if (type === messageTypes.showXBlockEditorModal) { - setShowOverlay(true); - // document.body.style.overflow = 'hidden'; - } - - if (type === messageTypes.hideXBlockEditorModal) { - setShowOverlay(false); - // document.body.style.overflow = 'auto'; - } - if (type && messageHandlers[type]) { messageHandlers[type](payload); } @@ -189,29 +177,24 @@ const XBlockContainerIframe: FC = ({ if (deleteXblockId) { unitXBlockActions.handleDelete(deleteXblockId); closeDeleteModal(); - // TODO: this artificial delay is a temporary solution - // to ensure the iframe content is properly refreshed. - setTimeout(() => { - sendMessageToIframe(messageTypes.refreshXBlock, null); - }, 1000); + handleRefreshXBlocks(); } }; const onConfigureSubmit = (...args: any[]) => { if (editXblockId) { handleConfigureSubmit(editXblockId, ...args, closeConfigureModal); - // TODO: this artificial delay is a temporary solution - // to ensure the iframe content is properly refreshed. - setTimeout(() => { - sendMessageToIframe(messageTypes.refreshXBlock, null); - }, 1000); + handleRefreshXBlocks(); } }; return ( <> - {showOverlay && ( -
+ {showLegacyEditModal && ( + )} { + title: string; + className?: string; +} + +const SANDBOX_OPTIONS = [ + 'allow-forms', + 'allow-modals', + 'allow-popups', + 'allow-popups-to-escape-sandbox', + 'allow-presentation', + 'allow-same-origin', + 'allow-scripts', + 'allow-top-navigation-by-user-activation', +].join(' '); + +export const IFRAME_FEATURE_POLICY = ( + 'microphone *; camera *; midi *; geolocation *; encrypted-media *, clipboard-write *' +); + +const ModalIframe = forwardRef( + ({ title, className, ...props }, ref: ForwardedRef) => ( +