feat: [FC-0070] Create a new Studio view for rendering whole Unit in an iframe - #35587
Conversation
|
Thanks for the pull request, @UvgenGen! What's next?Please work through the following steps to get your changes ready for engineering review: 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. 🔘 Let us know that your PR is ready for review:Who will review my changes?This repository is currently maintained by Where can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:
When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
GlugovGrGlib
left a comment
There was a problem hiding this comment.
First review round passed, please update description and check review comments
| lastHeight = newHeight; | ||
| lastWidth = newWidth; | ||
|
|
||
| // Within the learning microfrontend the iframe resizes to match the |
There was a problem hiding this comment.
As this template is dedicated to be used in the authoring MFE only please rename all comments about learning MFE in it
| }()); | ||
| </script> | ||
| <script> | ||
| var beamer_config = { |
There was a problem hiding this comment.
Where was this copied from?
I'm not sure about its usage, but I think this beamer script should be removed from this CMS view.
There was a problem hiding this comment.
This is copied from base.html
There was a problem hiding this comment.
Let's remove it from this template
| name='course_rerun_handler'), | ||
| re_path(fr'^container/{settings.USAGE_KEY_PATTERN}$', contentstore_views.container_handler, | ||
| name='container_handler'), | ||
| re_path(fr'^container_iframe/{settings.USAGE_KEY_PATTERN}$', contentstore_views.container_handler_iframe, |
There was a problem hiding this comment.
container_embed might be a better name for a URL
Also, in case of change please update container_handler_iframe to container_embed_handler
| @login_required | ||
| def container_handler_iframe(request, usage_key_string): # pylint: disable=too-many-statements | ||
| """ | ||
| The restful handler for container xblock requests. |
There was a problem hiding this comment.
Please update docstring to match specific purpose of this view
| return HttpResponseBadRequest() | ||
|
|
||
| container_handler_context = get_container_handler_context(request, usage_key, course, xblock) | ||
| return render_to_response('container_iframe.html', container_handler_context) |
There was a problem hiding this comment.
Please rename template to container_chromeless.html to outline relation with LMS's courseware_chromeless
| 'container_handler', | ||
| 'component_handler' | ||
| 'component_handler', | ||
| 'container_handler_iframe' |
There was a problem hiding this comment.
Do you mind adding a comma after the last element in the list?
| renderAddXBlockComponents: function() { | ||
| var self = this; | ||
| if (self.options.canEdit) { | ||
| if (self.options.canEdit && !self.options.isIframePage) { |
There was a problem hiding this comment.
Can we update parameter isIframePage to isIframeEmbed
| TaggingDrawerUtils.openDrawer(taxonomyTagsWidgetUrl, contentId); | ||
| }, | ||
|
|
||
| showMoveXBlockModal: function(event) { |
There was a problem hiding this comment.
I can't exactly understand why some functions were completely removed from this file?
There was a problem hiding this comment.
Some methods were duplicated, so I just removed the extra ones
There was a problem hiding this comment.
Strange, can you assume what is the reason for it, have you noticed anything strange in behavior on the legacy Unit page in CMS after the removal of these functions?
There was a problem hiding this comment.
I assume these methods were duplicated by mistake during some rebasing/merging because they are equal.
There was a problem hiding this comment.
9a29b0e to
69b83f2
Compare
| if (this.options.isIframeEmbed) { | ||
| window.parent.postMessage( | ||
| { | ||
| type: 'duplicateXBlock', | ||
| payload: {} | ||
| }, document.referrer | ||
| ); | ||
| return | ||
| } |
There was a problem hiding this comment.
[optional]: At this stage we do not expect fully working functionality, but it seems to me that it would be great to provide minimal working functionality. Since now after any action we get the error like Failed to execute 'postMessage' on 'Window': Invalid target origin '' in a call to 'postMessage'. I suggest catching such errors and not blocking the execution of the JS code flow. My proposal applies to all syntactic constructions in this file that are related to postMessage. What are your thoughts?
| if (this.options.isIframeEmbed) { | |
| window.parent.postMessage( | |
| { | |
| type: 'duplicateXBlock', | |
| payload: {} | |
| }, document.referrer | |
| ); | |
| return | |
| } | |
| try { | |
| if (this.options.isIframeEmbed) { | |
| window.parent.postMessage( | |
| { | |
| type: 'duplicateXBlock', | |
| payload: {} | |
| }, document.referrer | |
| ); | |
| return | |
| } | |
| } catch (e) { | |
| console.error(e); | |
| } |
69b83f2 to
b9456e2
Compare
| var lastHeight = window.parent.offsetHeight; | ||
| var lastWidth = window.parent.offsetWidth; |
There was a problem hiding this comment.
[nit]: During rendering of the iframe I noticed that we get errors related to the fact that the window.parent offsetHeight and offsetWidth values are not defined. They are inside the object, for example:
| var lastHeight = window.parent.offsetHeight; | |
| var lastWidth = window.parent.offsetWidth; | |
| var lastHeight = window.parent[0].offsetHeight; | |
| var lastWidth = window.parent[0].offsetWidth; |
| payload: {} | ||
| }, document.referrer | ||
| ); | ||
| return |
There was a problem hiding this comment.
[nit/inform]: We also decided that in future changes we will switch the processing of logic of different methods using conditions related to types of iframe post messages (for example, for some xblocks we want to show new modal windows, we plan to send the type of post message, for example "newModalWindow").
Let's remove return in all such constructions 💯
ea1ad59 to
c0f065d
Compare
|
[request/inform]: @GlugovGrGlib @UvgenGen we decided that we want to remove the Paste Component button (and the tooltip with information about the component) from the iframe, since we already have a ready-made functionality for inserting an xblock on the page in which the iframe is embedded.
|
c0f065d to
62b5ec6
Compare
| %button-styles { | ||
| width: 44px; | ||
| height: 44px; | ||
| border-radius: 50%; | ||
| } |
There was a problem hiding this comment.
[question] Do we need to declare %button-styles and other prepared style blocks in two files: _controls.scss and _course-unit-mfe-iframe.scss?
There was a problem hiding this comment.
No, I deleted it from _controls.scss
| padding: 24px; | ||
| padding-bottom: 12px; |
There was a problem hiding this comment.
[nit] Should be simplified to one padding rule.
Also, we can stick to general rules for margin/paddings with using $baseline variable (ex: https://github.com/openedx/edx-platform/blob/2bbd8ecd180fd299de2c4ee4bc1542ccb97b50bb/cms/static/sass/_base.scss#L313)
| padding: 24px; | ||
| padding-top: 12px; |
There was a problem hiding this comment.
[nit] Should be simplified to one padding rule.
| .wrapper-xblock .header-actions .actions-list .action-item .action-button { | ||
| color: $black; | ||
|
|
||
| @extend %button-styles; |
There was a problem hiding this comment.
[nit] Let's declare all extended rules at the top of the block for separation and simple overriding.
|
|
||
| .nav-item { | ||
| border-bottom: none; | ||
| color: #212529; |
There was a problem hiding this comment.
[question] Can we use existing variables or create new variables for colors instead of hardcoding them?
There was a problem hiding this comment.
Created some new variables
| } | ||
| } | ||
|
|
||
| input:not([type="radio"]):not([type="checkbox"]):not([type="submit"]) { |
There was a problem hiding this comment.
[nit] Can be simplified
input:not([type="radio"], [type="checkbox"], [type="submit"]) {
| body #openassessment-editor #openassessment_editor_header, | ||
| body #openassessment-editor .openassessment_tab_instructions { | ||
| background-color: $white; | ||
| } |
There was a problem hiding this comment.
[nit] Let's include in the block below:
body {
#openassessment-editor {
#openassessment_editor_header,
.openassessment_tab_instructions {
background-color: $white;
}
}
...
}
There was a problem hiding this comment.
I did some additional refactoring
- removed everything
!important - removed access to elements via
body
| // [class*="view-"] .modal-window .editor-with-buttons .xblock-actions { | ||
| // bottom: -6px; | ||
| // } |
There was a problem hiding this comment.
[question] Do we need this comments?
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" focusable="false" aria-hidden="true"> | ||
| <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25ZM21.41 6.34l-3.75-3.75-2.53 2.54 3.75 3.75 2.53-2.54Z" fill="currentColor"></path> | ||
| </svg> |
There was a problem hiding this comment.
I'll try to have a real review in the next day, but a quick question: where does this icon come from?
ormsbee
left a comment
There was a problem hiding this comment.
Some questions and requests. Nothing major. I don't feel like I can meaningfully review any of the styles though.
| Returns an HttpResponse with HTML content for the container xBlock. | ||
| The returned HTML is a chromeless rendering of the xBlock. |
There was a problem hiding this comment.
| Returns an HttpResponse with HTML content for the container xBlock. | |
| The returned HTML is a chromeless rendering of the xBlock. | |
| Returns an HttpResponse with HTML content for the container XBlock. | |
| The returned HTML is a chromeless rendering of the XBlock. |
[nit, optional]: The standard spelling is "XBlock".
| json: not currently supported | ||
| """ | ||
|
|
||
| from ..utils import get_container_handler_context |
There was a problem hiding this comment.
[question]: Is this to avoid a circular dependency?
There was a problem hiding this comment.
Yes, it is. I added a comment for it.
| container_handler_context = get_container_handler_context(request, usage_key, course, xblock) | ||
| return render_to_response('container_chromeless.html', container_handler_context) | ||
| else: | ||
| return HttpResponseBadRequest("Only supports HTML requests") |
There was a problem hiding this comment.
[request] If you have an error condition that can return early, please do that instead of doing an if-else and indenting the main logic of the function. It makes the primary flow of the code easier to read.
So in this case, something like:
if 'text/html' not in request.META.get('HTTP_ACCEPT', 'text/html'):
return HttpResponseBadRequest("Only supports HTML requests")
# rest of the function here.|
|
||
| from ..utils import get_container_handler_context | ||
|
|
||
| if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'): |
There was a problem hiding this comment.
[question] Are we worried about this getting hit with an http-accept header that only wants JSON? I don't usually see this kind of guard in a view that renders HTML.
There was a problem hiding this comment.
I created this function based on container_handler, but I agree that this check can be removed. Removed.
| try: | ||
| usage_key = UsageKey.from_string(usage_key_string) | ||
| except InvalidKeyError: # Raise Http404 on invalid 'usage_key_string' | ||
| raise Http404 # lint-amnesty, pylint: disable=raise-missing-from |
There was a problem hiding this comment.
[request] An invalid key should be a 400, not a 404, since it's a malformed request.
| try: | ||
| course, xblock, lms_link, preview_lms_link = _get_item_in_course(request, usage_key) | ||
| except ItemNotFoundError: | ||
| return HttpResponseBadRequest() |
There was a problem hiding this comment.
[request] If the UsageKey is well formed but not found, then this looks like the time to return the 404 error.
| display: inline-block; | ||
| } | ||
|
|
||
| #openassessment-editor #oa_rubric_editor_wrapper { |
There was a problem hiding this comment.
[question] Why do we need to bundle the ORA CSS here, and the Drag & Drop CSS below? This is a question, not a request to change this behavior at this time.
There was a problem hiding this comment.
Adding styles for ORA and Drag & Drop to this bundle is an improvement of visual perception for the new interface. We have two approaches for modal windows. The first is based on the react interface (full screen modal windows, for example for the Text component), the second is legacy modal windows. The current bundle provides styles exclusively for legacy modal windows inside the iframe that is embedded on the course unit page inside MFE Authoring. For the original legacy page, changing the styles of modal windows will not be available (their display will not change).
Also based on the fact that these xblocks are enabled by default and the code for these xblocks is outside the platform's code structure, additional styles were added.
| @@ -0,0 +1,279 @@ | |||
| ## coding=utf-8 | |||
There was a problem hiding this comment.
[request] Please add some sort of comment to this file to explain what it is, why it exists, and how it's intended to be different from the template file it's based off of.
There was a problem hiding this comment.
Added comment with a description
| <%static:include path="common/templates/image-modal.underscore" /> | ||
| </script> | ||
| <link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" /> | ||
| % if not settings.STUDIO_FRONTEND_CONTAINER_URL: |
There was a problem hiding this comment.
[request] Please add a comment explaining why we're doing this.
| ); | ||
| </%static:webpack> | ||
| </body> | ||
| <script type="text/javascript"> |
There was a problem hiding this comment.
[request] There's a lot of boilerplate in this template, along with stuff we probably don't strictly need (like the hotjar piece). That's okay–this isn't intended to be long-lived, and it's a fast way to get up and running. But please use comments to highlight the parts of this template that are specific to the new UI you're building (and not just copied from the older template).
There was a problem hiding this comment.
Added comment for specific script block and removed the hotjar piece.
|
@UvgenGen: Can you please squash + rebase? The static analysis bit should work after that. I just want to make sure it's not masking any other issues. Do you need me to merge these for you, or does someone on your team have merge rights for edx-platform? |
47d1c02 to
1b09b12
Compare
|
@ormsbee PR updated and squashed. I would appreciate your help with merging. |
GlugovGrGlib
left a comment
There was a problem hiding this comment.
One last comment to fix before merging it
|
|
||
|
|
||
| @require_GET | ||
| @login_required |
There was a problem hiding this comment.
It seems like we are missing a decorator that is required to use this view in an Iframe. Please add xframe_options_exempt as it is done in LMS: https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/courseware/views/views.py#L1546.
There was a problem hiding this comment.
xframe_options_exempt decorator added.
1b09b12 to
9c2d0da
Compare
|
2U Release Notice: This PR has been deployed to the edX staging environment in preparation for a release to production. |
|
2U Release Notice: This PR has been deployed to the edX production environment. |
1 similar comment
|
2U Release Notice: This PR has been deployed to the edX production environment. |

… with xBlocks controls
Description
Created a new Studio view for rendering the whole Unit in an iframe with xBlocks controls for studio MFE.
A new studio page renders all xBlocks in the unit with xBlock controls
Studio page sends postmessages to the host in case of size change and executing control actions
Issue: openedx/platform-roadmap#321
Supporting information
URL example: http://studio.local.edly.io:8001/container_embed/block-v1:test+test+test+type@vertical+block@47ca674b3f0f46d7a8be12d8fcccd232
Result:

Added tests for
container_embed_handler. Runpytest /cms/djangoapps/contentstore/views/tests/test_container_page.pyfor local testing inside LMS container.Deadline
Other information