Files
frontend-app-learning/src/courseware/course/sequence/Unit/ContentIFrame.jsx
Ben Warzeski 2e641ac6c9 Bw/unit splitup (#1134)
* refactor: break Unit component into smaller unit-tested parts

* feat: save scroll position on video fullscreen exit

* chore: remove swap file
2023-07-10 10:29:56 -04:00

115 lines
2.9 KiB
JavaScript

import PropTypes from 'prop-types';
import React from 'react';
import { ErrorPage } from '@edx/frontend-platform/react';
import { StrictDict } from '@edx/react-unit-test-utils';
import { Modal } from '@edx/paragon';
import PageLoading from '../../../../generic/PageLoading';
import * as hooks from './hooks';
/**
* Feature policy for iframe, allowing access to certain courseware-related media.
*
* We must use the wildcard (*) origin for each feature, as courseware content
* may be embedded in external iframes. Notably, xblock-lti-consumer is a popular
* block that iframes external course content.
* 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 IFRAME_FEATURE_POLICY = (
'microphone *; camera *; midi *; geolocation *; encrypted-media *, clipboard-write *'
);
export const testIDs = StrictDict({
contentIFrame: 'content-iframe-test-id',
modalIFrame: 'modal-iframe-test-id',
});
const ContentIFrame = ({
iframeUrl,
shouldShowContent,
loadingMessage,
id,
elementId,
onLoaded,
title,
}) => {
const {
handleIFrameLoad,
hasLoaded,
iframeHeight,
showError,
} = hooks.useIFrameBehavior({
elementId,
id,
iframeUrl,
onLoaded,
});
const {
modalOptions,
handleModalClose,
} = hooks.useModalIFrameData();
const contentIFrameProps = {
id: elementId,
src: iframeUrl,
allow: IFRAME_FEATURE_POLICY,
allowFullScreen: true,
height: iframeHeight,
scrolling: 'no',
referrerPolicy: 'origin',
onLoad: handleIFrameLoad,
};
return (
<>
{(shouldShowContent && !hasLoaded) && (
showError ? <ErrorPage /> : <PageLoading srMessage={loadingMessage} />
)}
{shouldShowContent && (
<div className="unit-iframe-wrapper">
<iframe title={title} {...contentIFrameProps} data-testid={testIDs.contentIFrame} />
</div>
)}
{modalOptions.open && (
<Modal
body={modalOptions.body
? <div className="unit-modal">{ modalOptions.body }</div>
: (
<iframe
title={modalOptions.title}
allow={IFRAME_FEATURE_POLICY}
frameBorder="0"
src={modalOptions.url}
style={{ width: '100%', height: '100vh' }}
/>
)}
dialogClassName="modal-lti"
onClose={handleModalClose}
open
/>
)}
</>
);
};
ContentIFrame.propTypes = {
iframeUrl: PropTypes.string,
id: PropTypes.string.isRequired,
shouldShowContent: PropTypes.bool.isRequired,
loadingMessage: PropTypes.node.isRequired,
elementId: PropTypes.string.isRequired,
onLoaded: PropTypes.func,
title: PropTypes.node.isRequired,
};
ContentIFrame.defaultProps = {
iframeUrl: null,
onLoaded: () => ({}),
};
export default ContentIFrame;