diff --git a/lms/djangoapps/static_template_view/views.py b/lms/djangoapps/static_template_view/views.py index e22eca1dc9..3a13f9a881 100644 --- a/lms/djangoapps/static_template_view/views.py +++ b/lms/djangoapps/static_template_view/views.py @@ -46,7 +46,11 @@ def render(request, template): content_type, __ = mimetypes.guess_type(template) try: - return render_to_response('static_templates/' + template, {}, content_type=content_type) + context = {} + # This is necessary for the dialog presented with the TOS in /register + if template == 'honor.html': + context['allow_iframing'] = True + return render_to_response('static_templates/' + template, context, content_type=content_type) except TopLevelLookupException: raise Http404 diff --git a/lms/static/js/spec/student_account/register_spec.js b/lms/static/js/spec/student_account/register_spec.js index f32db1b0de..d035155ac7 100644 --- a/lms/static/js/spec/student_account/register_spec.js +++ b/lms/static/js/spec/student_account/register_spec.js @@ -6,7 +6,8 @@ 'common/js/spec_helpers/template_helpers', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/student_account/models/RegisterModel', - 'js/student_account/views/RegisterView' + 'js/student_account/views/RegisterView', + 'js/student_account/tos_modal' ], function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) { describe('edx.student.account.RegisterView', function() { @@ -178,7 +179,9 @@ type: 'checkbox', required: true, instructions: '', - restrictions: {} + restrictions: {}, + supplementalLink: '/honor', + supplementalText: 'Review the Terms of Service and Honor Code' } ] }; @@ -366,6 +369,50 @@ // Form button should be disabled on success. expect(view.$submitButton).toHaveAttr('disabled'); }); + + it('displays a modal with the terms of service', function() { + var $modal, + $content; + + createRegisterView(this); + + // Check there is no modal container initially + expect($('.tos-modal').length).toEqual(0); + + // And no modal is being displayed + expect($('body').hasClass('open-modal')).toBe(false); + + // Click TOS button + $('.checkbox-honor_code .supplemental-link a').click(); + + // TOS modal container has been added and is visible + $modal = $('.tos-modal'); + expect($modal.length).toEqual(1); + expect($modal).toBeVisible(); + expect($('body').hasClass('open-modal')).toBe(true); + + // The modal has a content area, a Close button and a title matching the TOS link + $content = $modal.find('.modal-content'); + expect($content.length).toEqual(1); + expect($content.find('.modal-close-button').text()).toEqual('Close'); + expect($content.find('#modal-header-text').text()).toEqual( + 'Terms of Service and Honor Code' + ); + + // The content area has an iframe displaying the TOS link + expect($content.find('iframe').length).toEqual(1); + expect($content.find('iframe').attr('src').endsWith('/honor')).toBe(true); + + // Click the close button + $('.modal-close-button').click(); + + // The modal is now hidden + expect($modal).toBeHidden(); + expect($('body').hasClass('open-modal')).toBe(false); + + // The iframe has been deleted + expect($content.find('iframe').length).toEqual(0); + }); }); }); }).call(this, define || RequireJS.define); diff --git a/lms/static/js/student_account/tos_modal.js b/lms/static/js/student_account/tos_modal.js new file mode 100644 index 0000000000..d4982833c5 --- /dev/null +++ b/lms/static/js/student_account/tos_modal.js @@ -0,0 +1,178 @@ +/** + * Modal for displaying the Terms of Service in the Register page. + */ + +(function($, gettext) { + 'use strict'; + + var focusableElementsSelector = [ + 'a[href], area[href], input:not([disabled]), select:not([disabled]),', + 'textarea:not([disabled]), button:not([disabled]), iframe, object, embed,', + '*[tabindex], *[contenteditable]' + ].join(' '); + + var disableTabIndexingOn = function(containerSelector) { + var $container = $(containerSelector), + $focusableItems = $container.find('*').filter(focusableElementsSelector).filter(':visible'); + $container.attr('aria-hidden', 'true'); + $focusableItems.attr('tabindex', '-1'); + }; + + var enableTabIndexingOn = function(containerSelector) { + var $container = $(containerSelector), + $focusableItems = $container.find('*').filter(focusableElementsSelector).filter(':visible'); + $container.attr('aria-hidden', 'false'); + $focusableItems.attr('tabindex', '0'); + }; + + var showModal = function(modalSelector) { + $(modalSelector).attr('aria-hidden', 'false'); + $(modalSelector).show(); + disableTabIndexingOn('.window-wrap'); + // Prevent scrolling of the background + $('body').addClass('open-modal'); + }; + + var hideModal = function(modalSelector, tosLinkSelector) { + $(modalSelector).attr('aria-hidden', 'true'); + $(modalSelector).hide(); + enableTabIndexingOn('.window-wrap'); + $('body').removeClass('open-modal'); + $(modalSelector).find('iframe').remove(); + $(tosLinkSelector).focus(); + }; + + var buildModal = function(modalClass, contentClass, closeButtonClass) { + // Create the modal container + var modalTitle = gettext('Terms of Service and Honor Code'), + closeLabel = gettext('Close'), + titleId = 'modal-header-text', + $modal = $('
', { + class: modalClass, + 'aria-hidden': 'true' + }), + $content = $('
', { + class: contentClass, + role: 'dialog', + 'aria-modal': 'true', + 'aria-labelledby': titleId + }), + $header = $('
', { + class: 'header' + }), + $closeButton = $('