Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: [AXIMST-719] Course unit - Xblock problems #213

Merged
merged 5 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/course-unit/course-xblock/CourseXBlock.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import XBlockMessages from './xblock-messages/XBlockMessages';
import RenderErrorAlert from './render-error-alert';
import { XBlockContent } from './xblock-content';
import messages from './messages';
import { extractStylesWithContent } from './utils';

const CourseXBlock = ({
id, title, type, unitXBlockActions, shouldScroll, userPartitionInfo,
Expand All @@ -59,6 +60,10 @@ const CourseXBlock = ({
? intl.formatMessage(messages.visibilityMessage, { selectedGroupsLabel: userPartitionInfo.selectedGroupsLabel })
: null;

const stylesWithContent = xblockIFrameHtmlAndResources
?.map(item => extractStylesWithContent(item.html))
.filter(styles => styles.length > 0);

useEffect(() => {
dispatch(fetchXBlockIFrameHtmlAndResourcesQuery(id));
}, []);
Expand Down Expand Up @@ -189,6 +194,7 @@ const CourseXBlock = ({
getHandlerUrl={getHandlerUrl}
view={xblockInstanceHtmlAndResources}
type={type}
stylesWithContent={stylesWithContent}
/>
)}
</>
Expand Down
2 changes: 2 additions & 0 deletions src/course-unit/course-xblock/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ export const blockViewShape = PropTypes.shape({
content: PropTypes.string.isRequired,
resources: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
});

export const STYLE_TAG_PATTERN = /<style[^>]*>([\s\S]*?)<\/style>/gi;
19 changes: 19 additions & 0 deletions src/course-unit/course-xblock/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { STYLE_TAG_PATTERN } from './constants';

/**
* Extracts content of <style> tags from the given HTML string.
* @param {string} htmlString - The HTML string to extract styles from.
* @returns {string[]} An array containing the content of <style> tags.
*/
// eslint-disable-next-line import/prefer-default-export
export function extractStylesWithContent(htmlString) {
const matches = [];
let match = STYLE_TAG_PATTERN.exec(htmlString);

while (match !== null) {
matches.push(match[1]); // Pushing content of <style> tag
match = STYLE_TAG_PATTERN.exec(htmlString);
}

return matches;
}
10 changes: 7 additions & 3 deletions src/course-unit/course-xblock/xblock-content/XBlockContent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { wrapBlockHtmlForIFrame } from './iframe-wrapper';
ensureConfig(['STUDIO_BASE_URL', 'SECURE_ORIGIN_XBLOCK_BOOTSTRAP_HTML_URL'], 'studio xblock component');

const XBlockContent = ({
view, type, getHandlerUrl, onBlockNotification,
view, type, getHandlerUrl, onBlockNotification, stylesWithContent,
}) => {
const iframeRef = useRef(null);
const [html, setHtml] = useState(null);
Expand All @@ -26,6 +26,7 @@ const XBlockContent = ({
view.resources,
getConfig().STUDIO_BASE_URL,
type,
stylesWithContent,
);

// Load the XBlock HTML into the IFrame:
Expand All @@ -37,7 +38,7 @@ const XBlockContent = ({

// Process the XBlock view:
processView();
}, [view, type]);
}, [view, type, stylesWithContent]);

useEffect(() => {
// Handle any messages we receive from the XBlock Runtime code in the IFrame.
Expand Down Expand Up @@ -111,7 +112,7 @@ const XBlockContent = ({
referrerPolicy="origin"
frameBorder={0}
scrolling="no"
onLoad={() => setIsLoading(!isLoading)}
onLoad={() => setIsLoading(false)}
sandbox={[
'allow-forms',
'allow-modals',
Expand All @@ -135,10 +136,13 @@ XBlockContent.propTypes = {
onBlockNotification: PropTypes.func,
view: fetchable(blockViewShape).isRequired,
type: PropTypes.oneOfType(Object.values(COMPONENT_TYPES)).isRequired,
// eslint-disable-next-line react/forbid-prop-types
stylesWithContent: PropTypes.array,
};

XBlockContent.defaultProps = {
onBlockNotification: null,
stylesWithContent: null,
};

export default XBlockContent;
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,23 @@ import {
* Only required for legacy XBlocks that don't declare their
* JS and CSS dependencies properly.
* @param type The XBlock's type (openassessment, discussion, video, etc.)
* @param stylesWithContent Custom CSS global for all XBlock's.
*/
export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBaseUrl, type) {
export default function wrapBlockHtmlForIFrame(
html,
sourceResources,
studioBaseUrl,
type,
stylesWithContent,
) {
const resources = normalizeResources(sourceResources);

/* Extract CSS resources. */
const cssUrls = filterAndExtractResources(resources, 'url', 'text/css');
const sheets = filterAndExtractResources(resources, 'text', 'text/css');

const additionalCssTags = stylesWithContent.flatMap(sheet => `<style>${sheet}</style>`).join('\n');

let cssTags = generateResourceTags(cssUrls, studioBaseUrl, type);
cssTags += sheets.map(sheet => `<style>${sheet}</style>`).join('\n');

Expand Down Expand Up @@ -62,6 +71,9 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase
<link rel="stylesheet" href="${studioBaseUrl}/static/studio/debug_toolbar/css/toolbar.css">
<link rel="stylesheet" href="${studioBaseUrl}/static/studio/css/vendor/html5-input-polyfills/number-polyfill.css">
<link rel="stylesheet" href="${studioBaseUrl}/static/studio/css/WordCloudBlockDisplay.css">
<link rel="stylesheet" href="${studioBaseUrl}/assets/courseware/v1/911f0d1ac424861100db6c65e2504841/asset-v1:edx+1245+2024+type@asset+block/cm_style_guide_demox.css">
<link rel="stylesheet" href="${studioBaseUrl}/static/studio/css/vendor/normalize.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200">
<link rel="stylesheet" href="XBlockIFrame.css">

<!-- JS scripts that can be used by XBlocks -->
Expand Down Expand Up @@ -243,18 +255,29 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase
let result = '';
let modifiedHtml = '';

modifiedHtml = modifyVoidHrefToPreventDefault(html);
// Due to the use of edx-platform scripts in MFE, it is necessary to ensure that the paths for static files
// and important data-attributes are correct.
modifiedHtml = modifiedHtml.replace(/url\(&#39;\/assets/g, `url('${studioBaseUrl}/assets`);
modifiedHtml = modifiedHtml.replace(/src="\/asset/g, `src="${studioBaseUrl}/asset`);
modifiedHtml = modifiedHtml.replace(/src=&#34;\/asset/g, `src=&#34;${studioBaseUrl}/asset`);
modifiedHtml = modifiedHtml.replace(/href="\/asset/g, `href="${studioBaseUrl}/asset`);
modifiedHtml = modifiedHtml.replace(/src=&#34;\/static\/studio/g, `src=&#34;${studioBaseUrl}/static/studio`);
modifiedHtml = modifiedHtml.replace(/src="\/static/g, `src="${studioBaseUrl}/static`);
modifiedHtml = modifiedHtml.replace(/data-target="\/preview\/xblock/g, `data-target="${studioBaseUrl}/preview/xblock`);
modifiedHtml = modifiedHtml.replace(/data-url="\/preview/g, `data-url="${studioBaseUrl}/preview`);
modifiedHtml = modifiedHtml.replace(/src="\/media/g, `src="${studioBaseUrl}/media`);
const relativeToAbsoluteXBlocksUrls = {
'url(&#39;/asset': `url('${studioBaseUrl}/asset`,
'src="/asset': `src="${studioBaseUrl}/asset`,
'src=&#34;/asset': `src=&#34;${studioBaseUrl}/asset`,
'href="/asset': `href="${studioBaseUrl}/asset`,
'src=&#34;/static': `src=&#34;${studioBaseUrl}/static`,
'src="/static': `src="${studioBaseUrl}/static`,
'data-target="/preview': `data-target="${studioBaseUrl}/preview`,
'data-url="/preview': `data-url="${studioBaseUrl}/preview`,
'src="/preview': `src="${studioBaseUrl}/preview`,
'src="/media': `src="${studioBaseUrl}/media`,
': "/asset': `: "${studioBaseUrl}/asset`,
': "/xblock': `: "${studioBaseUrl}/xblock`,
};

modifiedHtml = modifyVoidHrefToPreventDefault(html);

// Block that replaces relative urls with absolute urls
Object.entries(relativeToAbsoluteXBlocksUrls).forEach(([key, value]) => {
modifiedHtml = modifiedHtml.replaceAll(key, value);
});

if (
type === COMPONENT_TYPES.discussion
Expand All @@ -269,6 +292,7 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase
<meta charset="UTF-8">
${legacyIncludes}
${cssTags}
${additionalCssTags}
</head>
<body class="wrapper-xblock level-page studio-xblock-wrapper">
<article class="xblock-render">
Expand Down Expand Up @@ -298,6 +322,7 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase
<meta charset="UTF-8">
${legacyIncludes}
${cssTags}
${additionalCssTags}
</head>
<body class="wrapper-xblock level-page studio-xblock-wrapper">
<section class="wrapper-xblock is-collapsible level-element">
Expand All @@ -321,6 +346,7 @@ export default function wrapBlockHtmlForIFrame(html, sourceResources, studioBase
<meta charset="UTF-8">
${legacyIncludes}
${cssTags}
${additionalCssTags}
</head>
<body>
${modifiedHtml}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
.wrapper-xblock {
border: none;
}

body .poll-block-form-wrapper {
display: block;
}
2 changes: 1 addition & 1 deletion src/course-unit/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,5 +179,5 @@ export async function getXBlockIFrameData(itemId) {
export const getHandlerUrl = async (blockId) => {
const baseUrl = getConfig().STUDIO_BASE_URL;

return `${baseUrl}/xblock/${blockId}/handler/handler_name`;
return `${baseUrl}/preview/xblock/${blockId}/handler/handler_name`;
};
1 change: 0 additions & 1 deletion src/course-unit/data/thunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ export function setXBlockOrderListQuery(blockId, xblockListIds, restoreCallback)
export function fetchXBlockIFrameHtmlAndResourcesQuery(xblockId) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.adding));

try {
const xblockIFrameData = await getXBlockIFrameData(xblockId);
Expand Down
Loading