From df77ff7e15926081dba43f5181a4e9ba9b64c9bb Mon Sep 17 00:00:00 2001 From: Jillian Vogel Date: Wed, 16 Oct 2024 16:53:26 +1030 Subject: [PATCH] feat: adds Library Content button iframes the Authoring MFE's Library Content Picker --- cms/djangoapps/contentstore/helpers.py | 2 + .../rest_api/v1/serializers/vertical_block.py | 1 + cms/djangoapps/contentstore/utils.py | 12 +++++ .../contentstore/views/component.py | 16 +++++- cms/static/images/large-library_v2-icon.png | Bin 0 -> 584 bytes .../views/components/add_library_content.js | 51 ++++++++++++++++++ cms/static/js/views/components/add_xblock.js | 24 +++++---- cms/static/js/views/pages/container.js | 3 +- cms/static/sass/assets/_graphics.scss | 7 +++ cms/static/sass/elements/_drawer.scss | 22 ++++++-- cms/templates/container.html | 4 ++ 11 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 cms/static/images/large-library_v2-icon.png create mode 100644 cms/static/js/views/components/add_library_content.js diff --git a/cms/djangoapps/contentstore/helpers.py b/cms/djangoapps/contentstore/helpers.py index 9c82fb60597..627569fda2e 100644 --- a/cms/djangoapps/contentstore/helpers.py +++ b/cms/djangoapps/contentstore/helpers.py @@ -190,6 +190,8 @@ def xblock_type_display_name(xblock, default_display_name=None): # description like "Multiple Choice Problem", but that won't work if our 'block' argument is just the block_type # string ("problem"). return _('Problem') + elif category == 'library_v2': + return _('Library Content') component_class = XBlock.load_class(category) if hasattr(component_class, 'display_name') and component_class.display_name.default: return _(component_class.display_name.default) # lint-amnesty, pylint: disable=translation-of-non-string diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py index f2e8b6ef431..dffa23349d9 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py +++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py @@ -89,6 +89,7 @@ class ContainerHandlerSerializer(serializers.Serializer): unit_block_id = serializers.CharField(source="unit.location.block_id") subsection_location = serializers.CharField(source="subsection.location") course_sequence_ids = serializers.ListField(child=serializers.CharField()) + library_content_picker_url = serializers.CharField() def get_assets_url(self, obj): """ diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 214193918eb..90ff1fde3ab 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -427,6 +427,17 @@ def get_course_outline_url(course_locator) -> str: return course_outline_url +def get_library_content_picker_url(block_locator) -> str: + """ + Gets course authoring microfrontend library content picker URL for the given parent block. + """ + course_locator = block_locator.course_key + mfe_base_url = get_course_authoring_url(course_locator) + content_picker_url = f'{mfe_base_url}/component-picker?parentLocator={block_locator}' + + return content_picker_url + + def get_unit_url(course_locator, unit_locator) -> str: """ Gets course authoring microfrontend URL for unit page view. @@ -2046,6 +2057,7 @@ def get_container_handler_context(request, usage_key, course, xblock): # pylint 'user_clipboard': user_clipboard, 'is_fullwidth_content': is_library_xblock, 'course_sequence_ids': course_sequence_ids, + 'library_content_picker_url': get_library_content_picker_url(xblock.location), } return context diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 36a5782aea5..6404258de0a 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -41,7 +41,16 @@ log = logging.getLogger(__name__) # NOTE: This list is disjoint from ADVANCED_COMPONENT_TYPES -COMPONENT_TYPES = ['discussion', 'library', 'html', 'openassessment', 'problem', 'video', 'drag-and-drop-v2'] +COMPONENT_TYPES = [ + 'discussion', + 'library', + 'library_v2', + 'html', + 'openassessment', + 'problem', + 'video', + 'drag-and-drop-v2', +] ADVANCED_COMPONENT_TYPES = sorted({name for name, class_ in XBlock.load_classes()} - set(COMPONENT_TYPES)) @@ -95,6 +104,10 @@ def _load_mixed_class(category): """ Load an XBlock by category name, and apply all defined mixins """ + # Libraries v2 content doesn't have an XBlock. + if category == 'library_v2': + return None + component_class = XBlock.load_class(category) mixologist = Mixologist(settings.XBLOCK_MIXINS) return mixologist.mix(component_class) @@ -216,6 +229,7 @@ def create_support_legend_dict(): 'video': _("Video"), 'openassessment': _("Open Response"), 'library': _("Legacy Library"), + 'library_v2': _("Library Content"), 'drag-and-drop-v2': _("Drag and Drop"), } diff --git a/cms/static/images/large-library_v2-icon.png b/cms/static/images/large-library_v2-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5242104c4ce6703d8ff3f8a53ea6fc8136b460fb GIT binary patch literal 584 zcmV-O0=NB%P)o8o|Asm< zyq~CgJJj3f9$)qhKya-JVvE4e6@WXuzo~jl)O#U-%mkPLnE^Lk0@MJCAa?<7gM9*J z_yB0zHR8HZv-=uwc}bT5`W440?oym$&rwd<(I)!<4CEOdKB!KwFsO}kc}W0tc3B+b zEgrGXNt~hy00{mP0e`6Tf}AN%r_8$F#smNvFd+PNGw+Xo0SK!Y2$ldmA<#z^>>2=W zY5^eP_ZtXy9wUZkGN4-qa%2`E^h=Z&S_2?r_@w|uiJ>U~ImFN+fTUt*5kOKgGzTEH z7+M98Obkr|NGXPv0fY`?2RlQ{0FsEISpccT&^iFQl#yeyU*w`>KK6iQ8h{eR4cT>!y!%qskt?oyHEZwsb}2Ng_`w zlQZDh0~X0000 + *
+ * ``` + */ +define(['jquery'], +function($) { + 'use strict'; + + const closePicker = (picker, pickerCover) => { + $(pickerCover).css('display', 'none'); + $(picker).empty(); + $(picker).css('display', 'none'); + $('body').removeClass('picker-open'); + }; + + const openPicker = (contentPickerUrl, picker, pickerCover) => { + // Add event listen to close picker when the iframe tells us to + window.addEventListener("message", function (event) { + if (event.data === 'closeComponentPicker') { + closePicker(picker, pickerCover); + } + }.bind(this)); + + $(pickerCover).css('display', 'block'); + // xss-lint: disable=javascript-jquery-html + $(picker).html( + `` + ); + $(picker).css('display', 'block'); + + // Prevent background from being scrollable when picker is open + $('body').addClass('picker-open'); + }; + + const createComponent = (contentPickerUrl) => { + const picker = document.querySelector("#library-content-picker"); + const pickerCover = document.querySelector(".picker-cover"); + + return openPicker(contentPickerUrl, picker, pickerCover); + }; + + return { + createComponent: createComponent, + }; +}); diff --git a/cms/static/js/views/components/add_xblock.js b/cms/static/js/views/components/add_xblock.js index 5f491116693..0feccab0ab7 100644 --- a/cms/static/js/views/components/add_xblock.js +++ b/cms/static/js/views/components/add_xblock.js @@ -3,8 +3,9 @@ */ define(['jquery', 'underscore', 'gettext', 'js/views/baseview', 'common/js/components/utils/view_utils', 'js/views/components/add_xblock_button', 'js/views/components/add_xblock_menu', + 'js/views/components/add_library_content', 'edx-ui-toolkit/js/utils/html-utils'], -function($, _, gettext, BaseView, ViewUtils, AddXBlockButton, AddXBlockMenu, HtmlUtils) { +function($, _, gettext, BaseView, ViewUtils, AddXBlockButton, AddXBlockMenu, AddLibraryContent, HtmlUtils) { 'use strict'; var AddXBlockComponent = BaseView.extend({ @@ -67,14 +68,19 @@ function($, _, gettext, BaseView, ViewUtils, AddXBlockButton, AddXBlockMenu, Htm oldOffset = ViewUtils.getScrollOffset(this.$el); event.preventDefault(); this.closeNewComponent(event); - ViewUtils.runOperationShowingMessage( - gettext('Adding'), - _.bind(this.options.createComponent, this, saveData, $element) - ).always(function() { - // Restore the scroll position of the buttons so that the new - // component appears above them. - ViewUtils.setScrollOffset(self.$el, oldOffset); - }); + + if (saveData.type === 'library_v2') { + AddLibraryContent.createComponent(this.options.libraryContentPickerUrl); + } else { + ViewUtils.runOperationShowingMessage( + gettext('Adding'), + _.bind(this.options.createComponent, this, saveData, $element), + ).always(function() { + // Restore the scroll position of the buttons so that the new + // component appears above them. + ViewUtils.setScrollOffset(self.$el, oldOffset); + }); + } } }); diff --git a/cms/static/js/views/pages/container.js b/cms/static/js/views/pages/container.js index 7e1f6de3b65..4713e8329bc 100644 --- a/cms/static/js/views/pages/container.js +++ b/cms/static/js/views/pages/container.js @@ -212,7 +212,8 @@ function($, _, Backbone, gettext, BasePage, var component = new AddXBlockComponent({ el: element, createComponent: _.bind(self.createComponent, self), - collection: self.options.templates + collection: self.options.templates, + libraryContentPickerUrl: self.options.libraryContentPickerUrl, }); component.render(); }); diff --git a/cms/static/sass/assets/_graphics.scss b/cms/static/sass/assets/_graphics.scss index 2230e0aca7e..13d6d83ec23 100644 --- a/cms/static/sass/assets/_graphics.scss +++ b/cms/static/sass/assets/_graphics.scss @@ -66,3 +66,10 @@ height: ($baseline*3); background: url('#{$static-path}/images/large-library-icon.png') center no-repeat; } + +.large-library_v2-icon { + display: inline-block; + width: ($baseline*3); + height: ($baseline*3); + background: url('#{$static-path}/images/large-library_v2-icon.png') center no-repeat; +} diff --git a/cms/static/sass/elements/_drawer.scss b/cms/static/sass/elements/_drawer.scss index 96edfe1983f..75355b6d7a3 100644 --- a/cms/static/sass/elements/_drawer.scss +++ b/cms/static/sass/elements/_drawer.scss @@ -1,7 +1,8 @@ // studio - elements - side drawers // ==================== -.drawer-cover { +.drawer-cover, +.picker-cover { @extend %ui-depth3; display: none; @@ -13,7 +14,8 @@ background: rgba(0, 0, 0, 0.8); } -.drawer-cover.gray-cover { +.drawer-cover.gray-cover, +.picker-cover.gray-cover { background: rgba(112, 112, 112, 0.8); } @@ -29,6 +31,20 @@ background-color: $gray-l4; } -body.drawer-open { +body.drawer-open, +body.picker-open { overflow: hidden; } + +.picker { + @extend %ui-depth4; + + display: none; + position: fixed; + top: 1em; + left: 1em; + right: 1em; + bottom: 1em; + background-color: $gray-l4; +} + diff --git a/cms/templates/container.html b/cms/templates/container.html index 058587e6bbe..8551039bcca 100644 --- a/cms/templates/container.html +++ b/cms/templates/container.html @@ -52,6 +52,7 @@ isUnitPage: ${is_unit_page | n, dump_js_escaped_json}, canEdit: true, outlineURL: "${outline_url | n, js_escaped_string}", + libraryContentPickerUrl: "${library_content_picker_url | n, js_escaped_string}", clipboardData: ${user_clipboard | n, dump_js_escaped_json}, } ); @@ -299,4 +300,7 @@
${_("Location ID")}
+ +
+