* View a unit page, which has its own URL * Components appear within a unit as full previews. Their top bar shows type icon and title on the left, and draft status (if any), tag count, overflow menu, and drag handle on the right. * Components have an overflow menu within a unit * Components can be selected within a unit * When components are selected, the standard component sidebar appears. The preview tab is hidden, since component previews are visible in the main content area. * Components within a unit full-page view have hover and selected states * Unit sidebar preview. * Frontend implementation Drag-n-drop components to reorder them in unit.
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
import { useIntl } from '@edx/frontend-platform/i18n';
|
|
import { getConfig } from '@edx/frontend-platform';
|
|
|
|
import messages from './messages';
|
|
import { IFRAME_FEATURE_POLICY } from '../../constants';
|
|
import { useIframeBehavior } from '../../generic/hooks/useIframeBehavior';
|
|
import { useIframe } from '../../generic/hooks/context/hooks';
|
|
import { useIframeContent } from '../../generic/hooks/useIframeContent';
|
|
|
|
export type VersionSpec = 'published' | 'draft' | number;
|
|
|
|
interface LibraryBlockProps {
|
|
onBlockNotification?: (event: { eventType: string; [key: string]: any }) => void;
|
|
usageKey: string;
|
|
version?: VersionSpec;
|
|
view?: string;
|
|
scrolling?: string;
|
|
}
|
|
/**
|
|
* React component that displays an XBlock in a sandboxed IFrame.
|
|
*
|
|
* The IFrame is resized responsively so that it fits the content height.
|
|
*
|
|
* We use an IFrame so that the XBlock code, including user-authored HTML,
|
|
* cannot access things like the user's cookies, nor can it make GET/POST
|
|
* requests as the user. However, it is allowed to call any XBlock handlers.
|
|
*/
|
|
export const LibraryBlock = ({
|
|
onBlockNotification,
|
|
usageKey,
|
|
version,
|
|
view,
|
|
scrolling = 'no',
|
|
}: LibraryBlockProps) => {
|
|
const { iframeRef, setIframeRef } = useIframe();
|
|
const xblockView = view ?? 'student_view';
|
|
|
|
const studioBaseUrl = getConfig().STUDIO_BASE_URL;
|
|
|
|
const intl = useIntl();
|
|
const queryStr = version ? `?version=${version}` : '';
|
|
const iframeUrl = `${studioBaseUrl}/xblocks/v2/${usageKey}/embed/${xblockView}/${queryStr}`;
|
|
const { iframeHeight } = useIframeBehavior({
|
|
id: usageKey,
|
|
iframeUrl,
|
|
iframeRef,
|
|
onBlockNotification,
|
|
});
|
|
|
|
useIframeContent(iframeRef, setIframeRef);
|
|
|
|
return (
|
|
<iframe
|
|
ref={iframeRef}
|
|
title={intl.formatMessage(messages.iframeTitle)}
|
|
src={iframeUrl}
|
|
data-testid="block-preview"
|
|
name={`xblock-iframe-${usageKey}`}
|
|
id={`xblock-iframe-${usageKey}`}
|
|
frameBorder="0"
|
|
loading="lazy"
|
|
referrerPolicy="origin"
|
|
style={{
|
|
width: '100%', height: iframeHeight, pointerEvents: 'auto', minHeight: '700px',
|
|
}}
|
|
allow={IFRAME_FEATURE_POLICY}
|
|
allowFullScreen
|
|
scrolling={scrolling}
|
|
/>
|
|
);
|
|
};
|