From 37ccfd730e367906c6045af803b80293f7c57005 Mon Sep 17 00:00:00 2001 From: Ihor Romaniuk Date: Thu, 27 Aug 2026 09:52:41 +0200 Subject: [PATCH 1/4] fix: fill the fullscreen editor modal with the block Expanding the component editor to fullscreen left the block sitting in the top 70% of the modal with empty space beneath it, and a block taller than the modal could not be reached at all: the frame was sized to the height the block reports, so flexbox shrank it while the modal itself does not scroll. In fullscreen the frame now stretches to whatever height the modal leaves it and a tall block scrolls inside it. The sized modal keeps sizing the frame to the block, as before. --- src/editors/AdvancedEditor.test.tsx | 22 ++++++++++++++++++- src/editors/AdvancedEditor.tsx | 1 + .../LibraryBlock/LibraryBlock.tsx | 7 ++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/editors/AdvancedEditor.test.tsx b/src/editors/AdvancedEditor.test.tsx index 8a8e0a0a2b..0b90f3928e 100644 --- a/src/editors/AdvancedEditor.test.tsx +++ b/src/editors/AdvancedEditor.test.tsx @@ -8,10 +8,14 @@ import { act, fireEvent, } from '../testUtils'; +import { LibraryBlock } from '../library-authoring/LibraryBlock'; import AdvancedEditor from './AdvancedEditor'; jest.mock('./containers/EditorContainer', () => ({ - EditorModalWrapper: jest.fn(() =>
Advanced Editor Iframe
), + EditorModalWrapper: jest.fn(({ children }) =>
Advanced Editor Iframe{children}
), +})); +jest.mock('../library-authoring/LibraryBlock', () => ({ + LibraryBlock: jest.fn(() =>
Library Block
), })); const onCloseMock = jest.fn(); @@ -99,4 +103,20 @@ describe('AdvancedEditor', () => { expect(onCloseMock).not.toHaveBeenCalled(); }); + + it('lets the block fill the modal only in fullscreen', async () => { + render(); + + expect(LibraryBlock).toHaveBeenLastCalledWith( + expect.objectContaining({ fillContainer: false }), + expect.anything(), + ); + + fireEvent.click(await screen.findByRole('button', { name: 'Toggle Fullscreen' })); + + expect(LibraryBlock).toHaveBeenLastCalledWith( + expect.objectContaining({ fillContainer: true }), + expect.anything(), + ); + }); }); diff --git a/src/editors/AdvancedEditor.tsx b/src/editors/AdvancedEditor.tsx index a41092b2e7..82c40c647b 100644 --- a/src/editors/AdvancedEditor.tsx +++ b/src/editors/AdvancedEditor.tsx @@ -89,6 +89,7 @@ const AdvancedEditor = ({ usageKey, onClose }: AdvancedEditorProps) => { view="studio_view" scrolling="yes" minHeight="70vh" + fillContainer={isFullscreen} /> diff --git a/src/library-authoring/LibraryBlock/LibraryBlock.tsx b/src/library-authoring/LibraryBlock/LibraryBlock.tsx index 9dc971a272..623d2cf302 100644 --- a/src/library-authoring/LibraryBlock/LibraryBlock.tsx +++ b/src/library-authoring/LibraryBlock/LibraryBlock.tsx @@ -19,6 +19,7 @@ interface LibraryBlockProps { view?: string; scrolling?: string; minHeight?: string; + fillContainer?: boolean; scrollIntoView?: boolean; showTitle?: boolean; addHeight?: number; @@ -38,6 +39,7 @@ export const LibraryBlock = ({ version, view, minHeight, + fillContainer = false, scrolling = 'no', scrollIntoView = false, showTitle = false, @@ -99,9 +101,10 @@ export const LibraryBlock = ({ referrerPolicy="origin" style={{ width: '100%', - height: iframeHeight + addHeight, pointerEvents: 'auto', - minHeight, + ...(fillContainer + ? { flex: '1 1 auto', height: 'auto', minHeight: 0 } + : { height: iframeHeight + addHeight, minHeight }), }} allow={IFRAME_FEATURE_POLICY} allowFullScreen From 78a876c30df639aff124c64a73e20aa49c883901 Mon Sep 17 00:00:00 2001 From: Ihor Romaniuk Date: Thu, 27 Aug 2026 09:52:41 +0200 Subject: [PATCH 2/4] fix: give the fullscreen text editor the height it gained Expanding the text or game editor to fullscreen left the writing area where it was, with a band of empty space between it and the footer: the editor body is sized to three quarters of the modal body, and TinyMCE resolves its own height once when it is built and then keeps that value in an inline style. In fullscreen the modal body now hands its height down the column, so the editor body fills it and TinyMCE follows. Editors taller than the modal keep scrolling, now inside the body content rather than the body itself. The sized modal is unchanged. --- .../containers/EditorContainer/index.scss | 21 +++++++++++++++++++ .../containers/EditorContainer/index.tsx | 1 + 2 files changed, 22 insertions(+) diff --git a/src/editors/containers/EditorContainer/index.scss b/src/editors/containers/EditorContainer/index.scss index 27f72bb2fd..0620dd806c 100644 --- a/src/editors/containers/EditorContainer/index.scss +++ b/src/editors/containers/EditorContainer/index.scss @@ -4,3 +4,24 @@ overflow: visible; } } + +// Pass the fullscreen height down: flex-basis outranks h-75 and TinyMCE's inline height. +.editor-modal.pgn__modal-fullscreen { + .pgn__modal-body, + .pgn__modal-body-content, + .editor-body { + display: flex; + flex-direction: column; + } + + .pgn__modal-body-content, + .editor-body, + .tox-tinymce { + flex: 1 1 0; + min-height: 0; + } + + .pgn__modal-body-content { + overflow-y: auto; + } +} diff --git a/src/editors/containers/EditorContainer/index.tsx b/src/editors/containers/EditorContainer/index.tsx index 98b111c363..6d85b31f9a 100644 --- a/src/editors/containers/EditorContainer/index.tsx +++ b/src/editors/containers/EditorContainer/index.tsx @@ -46,6 +46,7 @@ export const EditorModalWrapper: React.FC void; isOpen onClose={onClose} title={title} + className="editor-modal" size={fullscreen ? 'fullscreen' : 'xl'} isOverflowVisible={false} hasCloseButton={false} From 527585653b6577d5287422b0edfdaa3ccf95a6ed Mon Sep 17 00:00:00 2001 From: Ihor Romaniuk Date: Thu, 27 Aug 2026 10:25:45 +0200 Subject: [PATCH 3/4] test: cover both sizing modes of the block frame --- .../LibraryBlock/LibraryBlock.test.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/library-authoring/LibraryBlock/LibraryBlock.test.tsx diff --git a/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx b/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx new file mode 100644 index 0000000000..e88ac652aa --- /dev/null +++ b/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx @@ -0,0 +1,35 @@ +import { initializeMocks, render, screen } from '../../testUtils'; +import { IframeProvider } from '../../generic/hooks/context/iFrameContext'; +import { LibraryBlock } from '.'; + +const usageKey = 'lb:Org:Lib:html:block-1'; + +const renderBlock = (props = {}) => + render( + + + , + ); + +describe('LibraryBlock', () => { + beforeEach(() => { + initializeMocks(); + }); + + it('sizes the frame to the height the block reports', async () => { + renderBlock(); + + const iframe = await screen.findByTestId('block-preview'); + expect(iframe.style.minHeight).toBe('70vh'); + expect(iframe.style.flex).toBe(''); + }); + + it('stretches the frame to its container when asked to', async () => { + renderBlock({ fillContainer: true }); + + const iframe = await screen.findByTestId('block-preview'); + expect(iframe.style.flex).toBe('1 1 auto'); + expect(iframe.style.height).toBe('auto'); + expect(iframe.style.minHeight).toBe('0'); + }); +}); From cc218f879f3a24c6727e9c118f3871370442df2b Mon Sep 17 00:00:00 2001 From: Ihor Romaniuk Date: Thu, 27 Aug 2026 12:29:57 +0200 Subject: [PATCH 4/4] =?UTF-8?q?test:=20address=20review=20=E2=80=94=20shar?= =?UTF-8?q?ed=20min-height=20constant,=20userEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants.ts | 2 ++ src/editors/AdvancedEditor.test.tsx | 8 +++++++- src/editors/AdvancedEditor.tsx | 4 +++- src/library-authoring/LibraryBlock/LibraryBlock.test.tsx | 5 +++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index b68d6d52cf..8b5a286de4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -92,6 +92,8 @@ export const REGEX_RULES = { * This policy was selected in conference with the edX Security Working Group. * Changes to it should be vetted by them (security@edx.org). */ +export const ADVANCED_EDITOR_MIN_HEIGHT = '70vh'; + export const IFRAME_FEATURE_POLICY = 'microphone *; camera *; midi *; geolocation *; encrypted-media *; clipboard-write *'; diff --git a/src/editors/AdvancedEditor.test.tsx b/src/editors/AdvancedEditor.test.tsx index 0b90f3928e..0364e4b647 100644 --- a/src/editors/AdvancedEditor.test.tsx +++ b/src/editors/AdvancedEditor.test.tsx @@ -1,4 +1,5 @@ import { getConfig } from '@edx/frontend-platform'; +import userEvent from '@testing-library/user-event'; import { render, @@ -9,6 +10,7 @@ import { fireEvent, } from '../testUtils'; import { LibraryBlock } from '../library-authoring/LibraryBlock'; +import messages from './messages'; import AdvancedEditor from './AdvancedEditor'; jest.mock('./containers/EditorContainer', () => ({ @@ -105,6 +107,7 @@ describe('AdvancedEditor', () => { }); it('lets the block fill the modal only in fullscreen', async () => { + const user = userEvent.setup(); render(); expect(LibraryBlock).toHaveBeenLastCalledWith( @@ -112,7 +115,10 @@ describe('AdvancedEditor', () => { expect.anything(), ); - fireEvent.click(await screen.findByRole('button', { name: 'Toggle Fullscreen' })); + const toggleButton = await screen.findByRole('button', { + name: messages.advancedEditorFullscreenButtonAlt.defaultMessage, + }); + await user.click(toggleButton); expect(LibraryBlock).toHaveBeenLastCalledWith( expect.objectContaining({ fillContainer: true }), diff --git a/src/editors/AdvancedEditor.tsx b/src/editors/AdvancedEditor.tsx index 82c40c647b..9a7eebf705 100644 --- a/src/editors/AdvancedEditor.tsx +++ b/src/editors/AdvancedEditor.tsx @@ -12,6 +12,8 @@ import { } from '@openedx/paragon'; import { Close, CloseFullscreen, OpenInFull } from '@openedx/paragon/icons'; +import { ADVANCED_EDITOR_MIN_HEIGHT } from '@src/constants'; + import { LibraryBlock } from '../library-authoring/LibraryBlock'; import { EditorModalWrapper } from './containers/EditorContainer'; import { ToastContext } from '../generic/toast-context'; @@ -88,7 +90,7 @@ const AdvancedEditor = ({ usageKey, onClose }: AdvancedEditorProps) => { usageKey={usageKey} view="studio_view" scrolling="yes" - minHeight="70vh" + minHeight={ADVANCED_EDITOR_MIN_HEIGHT} fillContainer={isFullscreen} /> diff --git a/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx b/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx index e88ac652aa..334473f106 100644 --- a/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx +++ b/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx @@ -1,4 +1,5 @@ import { initializeMocks, render, screen } from '../../testUtils'; +import { ADVANCED_EDITOR_MIN_HEIGHT } from '../../constants'; import { IframeProvider } from '../../generic/hooks/context/iFrameContext'; import { LibraryBlock } from '.'; @@ -7,7 +8,7 @@ const usageKey = 'lb:Org:Lib:html:block-1'; const renderBlock = (props = {}) => render( - + , ); @@ -20,7 +21,7 @@ describe('LibraryBlock', () => { renderBlock(); const iframe = await screen.findByTestId('block-preview'); - expect(iframe.style.minHeight).toBe('70vh'); + expect(iframe.style.minHeight).toBe(ADVANCED_EDITOR_MIN_HEIGHT); expect(iframe.style.flex).toBe(''); });