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 8a8e0a0a2b..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, @@ -8,10 +9,15 @@ import { act, fireEvent, } from '../testUtils'; +import { LibraryBlock } from '../library-authoring/LibraryBlock'; +import messages from './messages'; 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 +105,24 @@ describe('AdvancedEditor', () => { expect(onCloseMock).not.toHaveBeenCalled(); }); + + it('lets the block fill the modal only in fullscreen', async () => { + const user = userEvent.setup(); + render(); + + expect(LibraryBlock).toHaveBeenLastCalledWith( + expect.objectContaining({ fillContainer: false }), + expect.anything(), + ); + + const toggleButton = await screen.findByRole('button', { + name: messages.advancedEditorFullscreenButtonAlt.defaultMessage, + }); + await user.click(toggleButton); + + expect(LibraryBlock).toHaveBeenLastCalledWith( + expect.objectContaining({ fillContainer: true }), + expect.anything(), + ); + }); }); diff --git a/src/editors/AdvancedEditor.tsx b/src/editors/AdvancedEditor.tsx index a41092b2e7..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,8 @@ 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/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} diff --git a/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx b/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx new file mode 100644 index 0000000000..334473f106 --- /dev/null +++ b/src/library-authoring/LibraryBlock/LibraryBlock.test.tsx @@ -0,0 +1,36 @@ +import { initializeMocks, render, screen } from '../../testUtils'; +import { ADVANCED_EDITOR_MIN_HEIGHT } from '../../constants'; +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(ADVANCED_EDITOR_MIN_HEIGHT); + 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'); + }); +}); 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