diff --git a/lti_consumer/lti_consumer.py b/lti_consumer/lti_consumer.py index 84e8430d60765f355b5ed7543cb1df84de3b6513..5140862f5b630171974fc6449402869400e1a165 100644 --- a/lti_consumer/lti_consumer.py +++ b/lti_consumer/lti_consumer.py @@ -339,21 +339,21 @@ class LtiConsumerXBlock(StudioEditableXBlockMixin, XBlock): modal_height = Integer( display_name="Modal Height", help=( - "Enter the desired pixel height of the modal overlay which will contain the LTI tool. " + "Enter the desired viewport percentage height of the modal overlay which will contain the LTI tool. " "This setting is only used when Hide External Tool is set to False and " "LTI Launch Target is set to Modal." ), - default=405, + default=80, scope=Scope.settings ) modal_width = Integer( display_name="Modal Width", help=( - "Enter the desired pixel width of the modal overlay which will contain the LTI tool. " + "Enter the desired viewport percentage width of the modal overlay which will contain the LTI tool. " "This setting is only used when Hide External Tool is set to False and " "LTI Launch Target is set to Modal." ), - default=720, + default=80, scope=Scope.settings ) has_score = Boolean( @@ -834,7 +834,22 @@ class LtiConsumerXBlock(StudioEditableXBlockMixin, XBlock): 'ask_to_send_email': self.ask_to_send_email, 'button_text': self.button_text, 'inline_height': self.inline_height, - 'modal_height': self.modal_height, + 'modal_vertical_offset': self._get_modal_position_offset(self.modal_height), + 'modal_horizontal_offset': self._get_modal_position_offset(self.modal_width), 'modal_width': self.modal_width, 'accept_grades_past_due': self.accept_grades_past_due, } + + def _get_modal_position_offset(self, viewport_percentage): + """ + Returns the css position offset to apply to the modal window + element when launch_target is modal. This enables us to position + the modal window as a percentage of the viewport dimensions. + + Arguments: + viewport_percentage (int): The percentage of the viewport that the modal should occupy + + Returns: + float: The css position offset to apply to the modal window + """ + return (100 - viewport_percentage) / 2 diff --git a/lti_consumer/static/css/student.css b/lti_consumer/static/css/student.css index 68a24f9b0516bd09e1e2c30db0717afcaac259f0..9cb835c6095a48985bb221256509f5e6df8b24db 100644 --- a/lti_consumer/static/css/student.css +++ b/lti_consumer/static/css/student.css @@ -1 +1 @@ -.xblock-student_view.xblock-student_view-lti_consumer h2.problem-header{display:inline-block}.xblock-student_view.xblock-student_view-lti_consumer div.problem-progress{display:inline-block;padding-left:5px;color:#666;font-weight:100;font-size:1em}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer{margin:0 auto}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .wrapper-lti-link{font-size:14px;background-color:#f6f6f6;padding:20px}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .wrapper-lti-link .lti-link{margin-bottom:0;text-align:right}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .wrapper-lti-link .lti-link button{font-size:13px;line-height:20.72px}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .lti-modal{top:80px !important}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .lti-modal .inner-wrapper{height:100%;padding:0 0 0 0}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer form.ltiLaunchForm{display:none}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer iframe.ltiLaunchFrame{width:100%;height:100%;display:block;border:0px}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer h4.problem-feedback-label{font-weight:100;font-size:1em;font-family:"Source Sans", "Open Sans", Verdana, Geneva, sans-serif, sans-serif}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer div.problem-feedback{margin-top:5px;margin-bottom:5px}.lean-overlay{background:transparent;background-image:radial-gradient(circle at 50% 30%, rgba(0,0,0,0.4), rgba(0,0,0,0.6));display:none;height:100%;left:0;position:fixed;top:0;width:100%;z-index:100} +.xblock-student_view.xblock-student_view-lti_consumer h2.problem-header{display:inline-block}.xblock-student_view.xblock-student_view-lti_consumer div.problem-progress{display:inline-block;padding-left:5px;color:#666;font-weight:100;font-size:1em}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer{margin:0 auto}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .wrapper-lti-link{font-size:14px;background-color:#f6f6f6;padding:20px}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .wrapper-lti-link .lti-link{margin-bottom:0;text-align:right}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .wrapper-lti-link .lti-link button{font-size:13px;line-height:20.72px}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .lti-modal{position:fixed;opacity:0;z-index:11000;box-sizing:border-box;margin-left:0;height:auto;padding:8px;border-radius:3px;box-shadow:0 0 5px 0 rgba(0,0,0,0.4);background:#464646;color:#3c3c3c}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .lti-modal .close-modal{display:inline-block;position:absolute;right:2px;top:0px;z-index:100;cursor:pointer;padding:10px;background:transparent;border:none;text-align:center}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .lti-modal .close-modal .fa{font:normal normal normal 14px/1 FontAwesome;font-size:inherit}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer .lti-modal .inner-wrapper{height:100%;padding:0 0 0 0}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer form.ltiLaunchForm{display:none}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer iframe.ltiLaunchFrame{width:100%;height:100%;display:block;border:0px}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer h4.problem-feedback-label{font-weight:100;font-size:1em;font-family:"Source Sans", "Open Sans", Verdana, Geneva, sans-serif, sans-serif}.xblock-student_view.xblock-student_view-lti_consumer div.lti_consumer div.problem-feedback{margin-top:5px;margin-bottom:5px}.lean-overlay{background:transparent;background-image:radial-gradient(circle at 50% 30%, rgba(0,0,0,0.4), rgba(0,0,0,0.6));display:none;height:100%;left:0;position:fixed;top:0;width:100%;z-index:100} diff --git a/lti_consumer/static/js/xblock_lti_consumer.js b/lti_consumer/static/js/xblock_lti_consumer.js index 041343561f15eb681036374a41e7d2d236c3a9a4..8a8070593eae3d110df2903f081fe210dd382d86 100644 --- a/lti_consumer/static/js/xblock_lti_consumer.js +++ b/lti_consumer/static/js/xblock_lti_consumer.js @@ -27,16 +27,11 @@ function LtiConsumerXBlock(runtime, element) { $("#" + overlay_id).css({"display": "block", opacity: 0}); $("#" + overlay_id).fadeTo(200, o.overlay); $(modal_id).css({ - "display": "block", - "position": "fixed", - "opacity": 0, - "z-index": 11000, - "left": 50 + "%", - "margin-left": -(modal_width / 2) + "px", - "top": o.top + "px" + "display": "block" }); $(modal_id).fadeTo(200, 1); $(modal_id).attr('aria-hidden', false); + $('body').css('overflow', 'hidden'); e.preventDefault(); @@ -65,6 +60,7 @@ function LtiConsumerXBlock(runtime, element) { $("#" + overlay_id).fadeOut(200); $(modal_id).css({"display": "none"}); $(modal_id).attr('aria-hidden', true); + $('body').css('overflow', 'auto'); $trigger.focus(); } } diff --git a/lti_consumer/static/sass/student.scss b/lti_consumer/static/sass/student.scss index 0d96c38bf819f2b782927b0a59421f68a1b488a4..88df54431ac4ab7342c97f05abc42c984f46aa8d 100644 --- a/lti_consumer/static/sass/student.scss +++ b/lti_consumer/static/sass/student.scss @@ -31,7 +31,35 @@ } .lti-modal { - top: 80px !important; + position: fixed; + opacity: 0; + z-index: 11000; + box-sizing: border-box; + margin-left: 0; + height: auto; + padding: 8px; + border-radius: 3px; + box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.4); + background: #464646; + color: #3c3c3c; + + .close-modal { + display: inline-block; + position: absolute; + right: 2px; + top: 0px; + z-index: 100; + cursor: pointer; + padding: 10px; + background: transparent; + border: none; + text-align: center; + + .fa { + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + } + } .inner-wrapper { height: 100%; diff --git a/lti_consumer/templates/html/student.html b/lti_consumer/templates/html/student.html index 11a67995b3bd9c4f81ece446e9e85feeda8f0b67..421a42c314fbfa4dd39509e6ce871852fb921f17 100644 --- a/lti_consumer/templates/html/student.html +++ b/lti_consumer/templates/html/student.html @@ -50,7 +50,7 @@ id="${element_id}-lti-modal" class="modal lti-modal" aria-hidden="true" - style="width:${modal_width}px; height:${modal_height}px;" + style="width:${modal_width}%; left:${modal_horizontal_offset}%; top:${modal_vertical_offset}%; bottom:${modal_vertical_offset}%;" > <div class="inner-wrapper" role="dialog"> <button class="close-modal"> diff --git a/lti_consumer/tests/unit/test_lti_consumer.py b/lti_consumer/tests/unit/test_lti_consumer.py index 652e59fce948301bab9df1cdc9c6dab1c643faa1..fa2ebe2ebcca3520891a7a0e14f97fc9c7604bc0 100644 --- a/lti_consumer/tests/unit/test_lti_consumer.py +++ b/lti_consumer/tests/unit/test_lti_consumer.py @@ -671,9 +671,25 @@ class TestGetContext(TestLtiConsumerXBlock): context_keys = ( 'launch_url', 'element_id', 'element_class', 'launch_target', 'display_name', 'form_url', 'hide_launch', 'has_score', 'weight', 'module_score', 'comment', 'description', 'ask_to_send_username', - 'ask_to_send_email', 'button_text', 'modal_height', 'modal_width', 'accept_grades_past_due' + 'ask_to_send_email', 'button_text', 'modal_vertical_offset', 'modal_horizontal_offset', 'modal_width', + 'accept_grades_past_due' ) context = self.xblock._get_context_for_template() # pylint: disable=protected-access for key in context_keys: self.assertIn(key, context) + + +class TestGetModalPositionOffset(TestLtiConsumerXBlock): + """ + Unit tests for LtiConsumerXBlock._get_modal_position_offset() + """ + + def test_offset_calculation(self): + """ + Test `_get_modal_position_offset` returns the correct value + """ + offset = self.xblock._get_modal_position_offset(self.xblock.modal_height) # pylint: disable=protected-access + + # modal_height defaults to 80, so offset should equal 10 + self.assertEqual(offset, 10) diff --git a/setup.py b/setup.py index 3622d3d8c6b7c1c9c53baa9dfe4288608bcf8c74..8a286e51f436e39dc674240087cf63d0ccefcd84 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def package_data(pkg, roots): setup( name='lti_consumer-xblock', - version='1.0.2', + version='1.0.3', description='This XBlock implements the consumer side of the LTI specification.', packages=[ 'lti_consumer',