Commit 30e9b3cd by Braden MacDonald

Merge branch 'clarifications' into fix-hgse-upgrade-merged

parents caccfd1e 2bde5538
......@@ -115,15 +115,13 @@ class MCQBlock(SubmittingXBlockMixin, QuestionnaireAbstractBlock):
log.debug(u'MCQ submission result: %s', result)
return result
def author_edit_view(self, context):
def get_author_edit_view_fragment(self, context):
"""
The options for the 1-5 values of the Likert scale aren't child blocks but we want to
show them in the author edit view, for clarity.
"""
fragment = Fragment(u"<p>{}</p>".format(self.question))
self.render_children(context, fragment, can_reorder=True, can_add=False)
fragment.add_content(loader.render_template('templates/html/questionnaire_add_buttons.html', {}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/questionnaire-edit.css'))
return fragment
def validate_field_data(self, validation, data):
......@@ -194,7 +192,7 @@ class RatingBlock(MCQBlock):
{"display_name": dn, "value": val} for val, dn in zip(self.FIXED_VALUES, display_names)
] + super(RatingBlock, self).human_readable_choices
def author_edit_view(self, context):
def get_author_edit_view_fragment(self, context):
"""
The options for the 1-5 values of the Likert scale aren't child blocks but we want to
show them in the author edit view, for clarity.
......@@ -207,6 +205,4 @@ class RatingBlock(MCQBlock):
'accepted_statuses': [None] + [self.describe_choice_correctness(c) for c in "12345"],
}))
self.render_children(context, fragment, can_reorder=True, can_add=False)
fragment.add_content(loader.render_template('templates/html/questionnaire_add_buttons.html', {}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/questionnaire-edit.css'))
return fragment
......@@ -314,8 +314,9 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
'child_content': child_content,
'missing_dependency_url': self.has_missing_dependency and self.next_step_url,
}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/vendor/underscore-min.js'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/util.js'))
js_file = 'public/js/mentoring_{}_view.js'.format('assessment' if self.is_assessment else 'standard')
fragment.add_javascript_url(self.runtime.local_resource_url(self, js_file))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring.js'))
......@@ -731,7 +732,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', {
"url_name": self.url_name
}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-edit.css'))
self.include_theme_files(fragment)
return fragment
......@@ -746,7 +747,10 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', {
"url_name": self.url_name
}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-edit.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-tinymce-content.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/util.js'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring_edit.js'))
fragment.initialize_js('MentoringEditComponents')
return fragment
......
/* Some styling to make clarifications stand out a bit in
studio HTML edit view. */
.mce-content-body .pb-clarification {
color: #999;
font-size: 0.75em;
}
.mce-content-body .pb-clarification::before {
content: "(?)["
}
.mce-content-body .pb-clarification::after {
content: "]"
}
......@@ -173,3 +173,11 @@
float: right;
display: none;
}
.pb-clarification span.clarification i {
font-style: normal;
}
.pb-clarification span.clarification i:hover {
color: rgb(0, 159, 230);
}
......@@ -113,6 +113,8 @@ function MentoringBlock(runtime, element) {
}
}
ProblemBuilderUtil.transformClarifications(element);
if (data.mode === 'standard') {
MentoringStandardView(runtime, element, mentoring);
}
......
......@@ -17,5 +17,8 @@ function MentoringEditComponents(runtime, element) {
$(this).addClass('disabled');
}
});
ProblemBuilderUtil.transformClarifications(element);
runtime.listenTo('deleted-child', updateButtons);
}
function QuestionnaireEdit(runtime, element) {
'use strict';
ProblemBuilderUtil.transformClarifications(element);
}
window.ProblemBuilderUtil = {
transformClarifications: function(element) {
var $element = $(element);
var transformExisting = function(node) {
$('.pb-clarification', node).each(function() {
var item = $(this);
var content = item.html();
var clarification = $(
'<span class="clarification" tabindex="0" role="note" aria-label="Clarification">' +
'<i data-tooltip-show-on-click="true" class="fa fa-info-circle" aria-hidden="true"></i>' +
'<span class="sr"></span>' +
'</span>'
);
clarification.find('i').attr('data-tooltip', content);
clarification.find('span.sr').html(content);
item.empty().append(clarification);
});
};
// Transform all span.pb-clarifications already existing inside the element.
transformExisting($element);
// Transform all future span.pb-clarifications using mutation observer.
// It's only needed in the Studio when editing xblock children because the
// block's JS init function isn't called after edits in the Studio.
if (window.MutationObserver) {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
Array.prototype.forEach.call(mutation.addedNodes, function(node) {
transformExisting(node);
});
})
});
observer.observe($element[0], {childList: true, subtree: true});
}
}
};
......@@ -167,13 +167,21 @@ class QuestionnaireAbstractBlock(StudioEditableXBlockMixin, StudioContainerXBloc
return choice.content
return submission
def get_author_edit_view_fragment(self, context):
fragment = super(QuestionnaireAbstractBlock, self).author_edit_view(context)
return fragment
def author_edit_view(self, context):
"""
Add some HTML to the author view that allows authors to add choices and tips.
"""
fragment = super(QuestionnaireAbstractBlock, self).author_edit_view(context)
fragment = self.get_author_edit_view_fragment(context)
fragment.add_content(loader.render_template('templates/html/questionnaire_add_buttons.html', {}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/questionnaire-edit.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/util.js'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/questionnaire_edit.js'))
fragment.initialize_js('QuestionnaireEdit')
return fragment
def validate_field_data(self, validation, data):
......
......@@ -15,8 +15,8 @@
</div>
<label class="choice-label" for="choice-{{ self.html_id }}-{{i}}">
{{i}}
{% if i == '1' %} - {{ self.low }}{% endif %}
{% if i == '5' %} - {{ self.high }}{% endif %}
{% if i == '1' %} - {{ self.low|safe }}{% endif %}
{% if i == '5' %} - {{ self.high|safe }}{% endif %}
</label>
<div class="choice-tips-container">
<div class="choice-tips"></div>
......
{% load i18n %}
<p>{{ question }}</p>
<p>{{ question|safe }}</p>
<h2>{% trans "Built-in choices:" %}</h2>
<ul>
<li>Choice (1): <strong>1 - {{ low }}</strong> ({{accepted_statuses.1}})</li>
<li>Choice (1): <strong>1 - {{ low|safe }}</strong> ({{accepted_statuses.1}})</li>
<li>Choice (2): <strong>2</strong> ({{accepted_statuses.2}})</li>
<li>Choice (3): <strong>3</strong> ({{accepted_statuses.3}})</li>
<li>Choice (4): <strong>4</strong> ({{accepted_statuses.4}})</li>
<li>Choice (5): <strong>5 - {{ high }}</strong> ({{accepted_statuses.5}})</li>
<li>Choice (5): <strong>5 - {{ high|safe }}</strong> ({{accepted_statuses.5}})</li>
</ul>
<h2>{% trans "Additional custom choices and tips:" %}</h2>
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2015 Harvard, edX & OpenCraft
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
"""
Test that <span class="pb-clarification"> elements are transformed into LMS-like tooltips.
"""
# Imports ###########################################################
import ddt
from cgi import escape
from xblockutils.base_test import SeleniumXBlockTest
# Classes ###########################################################
@ddt.ddt
class ClarificationTest(SeleniumXBlockTest):
"""
Test that the content of span.pb-clarification elements is transformed into
tooltip elements.
"""
clarification_text = 'Let me clarify...'
mcq_template = """
<problem-builder>
<pb-mcq question="Who was your favorite character? {clarify_escaped}">
<pb-choice value="gaius">Gaius Baltar</pb-choice>
<pb-choice value="adama">Admiral William Adama {clarify}</pb-choice>
<pb-choice value="starbuck">Starbuck</pb-choice>
</pb-mcq>
</problem-builder>
"""
mrq_template = """
<problem-builder>
<pb-mrq question="What makes a great {clarify_escaped} MRQ {clarify_escaped}?">
<pb-choice value="1">Lots of choices</pb-choice>
<pb-choice value="2">Funny{clarify} choices</pb-choice>
<pb-choice value="3">Not sure {clarify}</pb-choice>
</pb-mrq>
</problem-builder>
"""
rating_template = """
<problem-builder>
<pb-rating name="rating_1_1" question="How do you rate {clarify_escaped} Battlestar Galactica?">
<pb-choice value="6">More than 5 stars {clarify}</pb-choice>
</pb-rating>
</problem-builder>
"""
long_answer_template = """
<problem-builder>
<pb-answer question="What did you think {clarify_escaped} of the ending?" />
</problem-builder>
"""
html_block_template = """
<problem-builder>
<html_demo><p>This is some raw {clarify} HTML code.</p></html_demo>
</problem-builder>
"""
def prepare_xml_scenario(self, xml_template):
span = '<span class="pb-clarification">{}</span>'.format(self.clarification_text)
escaped_span = escape(span, quote=True)
return xml_template.format(
clarify=span,
clarify_escaped=escaped_span
)
@ddt.data(
(mcq_template, 2),
(mrq_template, 4),
(rating_template, 2),
(long_answer_template, 1),
(html_block_template, 1),
)
@ddt.unpack
def test_title(self, xml_template, tooltip_count):
self.set_scenario_xml(self.prepare_xml_scenario(xml_template))
pb_element = self.go_to_view()
clarifications = pb_element.find_elements_by_css_selector('span.pb-clarification')
self.assertEqual(len(clarifications), tooltip_count)
for clarification in clarifications:
tooltip = clarification.find_element_by_css_selector('i[data-tooltip]')
self.assertEqual(tooltip.get_attribute('data-tooltip'), self.clarification_text)
sr = clarification.find_element_by_css_selector('span.sr')
self.assertEqual(sr.text, self.clarification_text)
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