Commit eaa02db6 by Douglas Cerna

Added Terms of Service modal

parent 74f9858b
......@@ -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
......
......@@ -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);
/**
* 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 = $('<div>', {
class: modalClass,
'aria-hidden': 'true'
}),
$content = $('<div>', {
class: contentClass,
role: 'dialog',
'aria-modal': 'true',
'aria-labelledby': titleId
}),
$header = $('<div>', {
class: 'header'
}),
$closeButton = $('<button>', {
'aria-label': closeLabel,
class: closeButtonClass
}),
$title = $('<h1>', {
id: titleId
});
$closeButton.text(closeLabel);
$title.text(modalTitle);
$header.append($title);
$header.append($closeButton);
$content.append($header);
$modal.append($content);
return $modal;
};
var buildIframe = function(link, modalSelector, contentSelector, tosLinkSelector) {
// Create an iframe with contents from the link and set its height to match the content area
return $('<iframe>', {
src: link.href,
load: function() {
var $iframeHead = $(this).contents().find('head'),
$iframeBody = $(this).contents().find('body');
// Overwrite styles in child page to hide top navigation and footer
var $style = $('<style>', {type: 'text/css'}),
styleContent = [
'/* Default honor.html template */',
'.nav-skip, header#global-navigation, .wrapper-footer {',
' display: none;',
'}',
'.container.about {',
' min-width: auto;',
'}',
'/* https://www.edx.org/edx-terms-service */',
'.edx-header, #skip-link, footer {',
' display: none;',
'}',
'.region-banner, #breadcrumb + .region-column-wrapper, .edx-header + .region-column-wrapper {',
' margin-top: 0;',
' padding-top: 10px;',
'}',
'body.node-type-page h1.field-page-tagline {',
' font-size: 16px;',
'}',
'/* edx-themes */',
'.page-heading, .footer-main {',
' display: none;',
'}'
].join('\n');
$style.text(styleContent);
$iframeHead.append($style);
// Set the iframe's height to fill the available space
$(this).css({
height: $(contentSelector).height()
});
// Hide the modal when ESC is pressed and the iframe is focused
$iframeBody.keydown(function(event) {
if ($(modalSelector).is(':visible') && event.keyCode === 27) {
event.preventDefault();
hideModal(modalSelector, tosLinkSelector);
}
});
}
});
};
$(document).ready(function() {
var tosLinkSelector = '.checkbox-honor_code .supplemental-link a',
closeButtonClass = 'modal-close-button',
closeButtonSelector = '.' + closeButtonClass,
contentClass = 'modal-content',
contentSelector = '.' + contentClass,
modalClass = 'tos-modal',
modalSelector = '.' + modalClass;
$('body').on('click', tosLinkSelector, function(event) {
var link = event.target,
$modal,
$iframe;
event.preventDefault();
// Ignore disabled TOS
if (link.href.endsWith('#')) {
return;
}
// Add the modal if it doesn't exist yet
if ($(modalSelector).length < 1) {
$modal = buildModal(modalClass, contentClass, closeButtonClass);
$('body').append($modal);
}
// Add a new iframe to the content area
$iframe = buildIframe(link, modalSelector, contentSelector, tosLinkSelector);
$(contentSelector).append($iframe);
showModal(modalSelector);
$(closeButtonSelector).focus();
});
$('body').on('click', closeButtonSelector, function() {
hideModal(modalSelector, tosLinkSelector);
});
// Hide the modal when clicking outside its content
$('body').on('click', modalSelector, function(event) {
if ($(event.target).hasClass(modalClass)) {
hideModal(modalSelector, tosLinkSelector);
}
});
// Hide the modal when ESC is pressed and the modal is focused
$(document).keydown(function(event) {
if ($(modalSelector).is(':visible') && event.keyCode === 27) {
event.preventDefault();
hideModal(modalSelector, tosLinkSelector);
}
});
});
}(jQuery, gettext));
......@@ -683,3 +683,91 @@
color: $gray-d2;
}
}
.tos-modal {
background-color: $black-t1;
display: none;
height: 120%;
left: -10%;
overflow: auto;
position: fixed;
top: -10%;
width: 120%;
z-index: 1;
.modal-content {
background-color: $white;
bottom: auto;
left: 50%;
margin: auto;
max-width: 650px;
min-height: 540px;
min-width: 300px;
position: fixed;
right: auto;
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
.header {
height: 50px;
position: relative;
h1#modal-header-text {
float: left;
font-family: $sans-serif;
font-size: font-size(large);
font-weight: bold;
margin-bottom: 0;
padding-left: 20px;
position: absolute;
text-align: left;
top: 50%;
transform: translate(0, -50%);
width: 75%;
}
.modal-close-button {
background: $white;
border: none;
border-radius: 0;
box-shadow: none;
box-sizing: border-box;
color: $uxpl-blue-base;
float: right;
font-size: font-size(large);
height: 48px;
letter-spacing: normal;
padding: 0;
position: absolute;
right: 0;
text-shadow: none;
text-transform: lowercase;
top: 50%;
transform: translate(0, -50%);
width: 80px;
}
.modal-close-button:hover {
background: $white;
color: $uxpl-blue-hover-active;
text-decoration: underline;
}
.modal-close-button:focus {
color: $uxpl-blue-hover-active;
text-decoration: underline;
}
}
iframe {
border: 0;
width: 100%;
}
}
}
body.open-modal {
overflow: hidden;
}
<%page expression_filter="h"/>
<%!
import json
from django.utils.translation import ugettext as _
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangolib.js_utils import dump_js_escaped_json
%>
<%namespace name='static' file='/static_content.html'/>
......@@ -22,6 +24,9 @@
newrelic.addPageAction('xfinished');
}
</%static:require_module>
% if configuration_helpers.get_value('DISPLAY_TOS_IN_MODAL_ON_REGISTRATION_PAGE', False):
<script type="text/javascript" src="${static.url('js/student_account/tos_modal.js')}"></script>
% endif
</%block>
<%block name="header_extras">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment