TNL-6920 Component Editor Efficiency Improvements

* Enlarges component modal for easier editing
* Allows display name to be edited in place
* Improves markdown button labeling
* Moves markdown cheatsheet to the edit modal, for quick reference
This commit is contained in:
Dave St.Germain
2019-08-28 15:11:27 -04:00
parent 1cc925b624
commit ea3232feac
15 changed files with 184 additions and 170 deletions

View File

@@ -48,7 +48,8 @@ CONTAINER_TEMPLATES = [
"add-xblock-component", "add-xblock-component-button", "add-xblock-component-menu",
"add-xblock-component-support-legend", "add-xblock-component-support-level", "add-xblock-component-menu-problem",
"xblock-string-field-editor", "xblock-access-editor", "publish-xblock", "publish-history",
"unit-outline", "container-message", "container-access", "license-selector",
"unit-outline", "container-message", "container-access", "license-selector", "copy-clipboard-button",
"edit-title-button",
]

View File

@@ -61,7 +61,7 @@ describe('EditXBlockModal', function() {
it('shows the correct title', function() {
var requests = AjaxHelpers.requests(this);
modal = showModal(requests, mockXBlockEditorHtml);
expect(modal.$('.modal-window-title').text()).toBe('Editing: Component');
expect(modal.$('.modal-window-title span.modal-button-title').text()).toBe('Editing: Component');
});
it('does not show any editor mode buttons', function() {
@@ -134,7 +134,7 @@ describe('EditXBlockModal', function() {
it('shows the correct title', function() {
var requests = AjaxHelpers.requests(this);
modal = showModal(requests, mockXModuleEditorHtml);
expect(modal.$('.modal-window-title').text()).toBe('Editing: Component');
expect(modal.$('.modal-window-title span.modal-button-title').text()).toBe('Editing: Component');
});
it('shows the correct default buttons', function() {

View File

@@ -90,6 +90,7 @@ installEditTemplates = function(append) {
// Add templates needed by the edit XBlock modal
TemplateHelpers.installTemplate('edit-xblock-modal');
TemplateHelpers.installTemplate('editor-mode-button');
TemplateHelpers.installTemplate('edit-title-button');
// Add templates needed by the settings editor
TemplateHelpers.installTemplate('metadata-editor');

View File

@@ -29,7 +29,7 @@ define(['jquery', 'common/js/spec_helpers/template_helpers', 'common/js/spec_hel
getModalTitle = function(modal) {
var modalElement = getModalElement(modal);
return modalElement.find('.modal-window-title').text();
return modalElement.find('.modal-window-title span.modal-button-title').text();
};
isShowingModal = function(modal) {

View File

@@ -63,6 +63,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/baseview'],
},
render: function() {
// xss-lint: disable=javascript-jquery-html
this.$el.html(this.modalTemplate({
name: this.options.modalName,
type: this.options.modalType,
@@ -83,6 +84,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/baseview'],
renderContents: function() {
var contentHtml = this.getContentHtml();
// xss-lint: disable=javascript-jquery-html
this.$('.modal-content').html(contentHtml);
},
@@ -146,6 +148,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/baseview'],
name: name,
isPrimary: isPrimary
});
// xss-lint: disable=javascript-jquery-append
this.getActionBar().find('ul').append(html);
},
@@ -178,8 +181,8 @@ define(['jquery', 'underscore', 'gettext', 'js/views/baseview'],
modalWindow = this.$el.find(this.options.modalWindowClass);
availableWidth = $(window).width();
availableHeight = $(window).height();
maxWidth = availableWidth * 0.80;
maxHeight = availableHeight * 0.80;
maxWidth = availableWidth * 0.98;
maxHeight = availableHeight * 0.98;
modalWidth = Math.min(modalWindow.outerWidth(), maxWidth);
modalHeight = Math.min(modalWindow.outerHeight(), maxHeight);

View File

@@ -11,7 +11,8 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_mod
var EditXBlockModal = BaseModal.extend({
events: _.extend({}, BaseModal.prototype.events, {
'click .action-save': 'save',
'click .action-modes a': 'changeMode'
'click .action-modes a': 'changeMode',
'click .title-edit-button': 'clickTitleButton'
}),
options: $.extend({}, BaseModal.prototype.options, {
@@ -40,6 +41,7 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_mod
this.xblockInfo = XBlockViewUtils.findXBlockInfo(xblockElement, rootXBlockInfo);
this.options.modalType = this.xblockInfo.get('category');
this.editOptions = options;
this.render();
this.show();
@@ -68,6 +70,11 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_mod
});
},
createTitleEditor: function(title) {
// xss-lint: disable=javascript-jquery-html
this.$('.modal-window-title').html(this.loadTemplate('edit-title-button')({title: title}));
},
onDisplayXBlock: function() {
var editorView = this.editorView,
title = this.getTitle(),
@@ -84,7 +91,7 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_mod
// Update the custom editor's title
editorView.$('.component-name').text(title);
} else {
this.$('.modal-window-title').text(title);
this.createTitleEditor(title);
if (editorView.getDataEditor() && editorView.getMetadataEditor()) {
this.addDefaultModes();
// If the plugins content element exists, add a button to reveal it.
@@ -103,8 +110,6 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_mod
}
this.getActionBar().show();
}
// Resize the modal to fit the window
this.resize();
},
@@ -146,7 +151,6 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_mod
},
changeMode: function(event) {
this.removeCheatsheetVisibility();
var $parent = $(event.target.parentElement),
mode = $parent.data('mode');
event.preventDefault();
@@ -207,16 +211,30 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_mod
}));
},
removeCheatsheetVisibility: function() {
var $cheatsheet = $('article.simple-editor-open-ended-cheatsheet');
if ($cheatsheet.length === 0) {
$cheatsheet = $('article.simple-editor-cheatsheet');
}
if ($cheatsheet.hasClass('shown')) {
$cheatsheet.removeClass('shown');
$('.modal-content').removeClass('cheatsheet-is-shown');
}
clickTitleButton: function(event) {
var self = this,
oldTitle = this.xblockInfo.get('display_name'),
titleElt = this.$('.modal-window-title'),
$input = $('<input type="text" size="40" />'),
changeFunc = function(evt) {
var newTitle = $(evt.target).val();
if (oldTitle !== newTitle) {
self.xblockInfo.set('display_name', newTitle);
self.xblockInfo.save({metadata: {display_name: newTitle}});
}
self.createTitleEditor(self.getTitle());
return true;
};
event.preventDefault();
$input.val(oldTitle);
$input.change(changeFunc).blur(changeFunc);
titleElt.html($input); // xss-lint: disable=javascript-jquery-html
$input.focus().select();
$(event.target).remove();
return true;
}
});
return EditXBlockModal;

View File

@@ -30,6 +30,7 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/pages/base_page
view: 'container_preview',
defaultViewClass: ContainerView,
// Overridable by subclasses-- determines whether the XBlock component

View File

@@ -219,6 +219,11 @@
color: $blue-d4;
}
}
.clipboard-button {
position: absolute;
right: 30px;
bottom: 30px;
}
}
}
@@ -248,7 +253,7 @@
// large modals - component editors and interactives
// ------------------------
.modal-lg {
width: 70%;
width: 95%;
min-width: ($baseline*27.5);
height: auto;
@@ -266,7 +271,7 @@
}
.editor-modes {
width: 48%;
width: 49%;
display: inline-block;
@include text-align(right);
@@ -378,7 +383,6 @@
}
}
// MODAL TYPE: component - video modal (includes special overrides for xblock-related editing view)
.modal-lg.modal-type-video {
.modal-content {

View File

@@ -0,0 +1,5 @@
<a href="#" class="button action-button clipboard-button" data-tooltip="<%- gettext('Copy Component Location') %>">
<i class="fa fa-link"></i>
<%- gettext('Copy Component Location') %>
<input class="sr" value="<%- location %>"/>
</a>

View File

@@ -0,0 +1 @@
<span class="modal-button-title"><%- title %></span> <button data-tooltip="<%- gettext('Edit Title') %>" class="btn-default action-edit title-edit-button"><span class="icon fa fa-pencil" aria-hidden="true"></span><span class="sr"> <%- gettext('Edit Title') %></span></button>

View File

@@ -3,10 +3,4 @@
<li class="field comp-setting-entry metadata_entry">
</li>
<% }) %>
<li class="field comp-setting-entry metadata_entry">
<div class="wrapper-comp-setting-text">
<label class="label setting-label"><%- gettext("Component Location ID") %></label>
<span class="setting-text"><%- locator %></span>
</div>
</li>
</ul>

View File

@@ -1,4 +1,5 @@
<%! from django.utils.translation import ugettext as _ %>
<%page expression_filter="h"/>
<%namespace name='static' file='../static_content.html'/>
<% isLaTexProblem='source_code' in editable_metadata_fields and editable_metadata_fields['source_code']['explicitly_set'] and enable_latex_compiler %>
@@ -18,6 +19,7 @@
<span class="problem-editor-icon heading3">
<img class="icon" src="${static.url('images/cms-editor_heading.png')}" alt="${_("Insert a heading")}">
</span>
${_("Heading")}
</button>
</li>
<li>
@@ -25,6 +27,7 @@
<span class="problem-editor-icon multiple-choice">
<img class="icon" src="${static.url('images/cms-editor_radio.png')}" alt="${_("Add a multiple choice question")}">
</span>
${_("Multiple Choice")}
</button>
</li>
<li>
@@ -32,6 +35,7 @@
<span class="problem-editor-icon checks">
<img class="icon" src="${static.url('images/cms-editor_checkbox.png')}" alt="${_("Add a question with checkboxes")}">
</span>
${_("Checkboxes")}
</button>
</li>
<li>
@@ -39,6 +43,7 @@
<span class="problem-editor-icon string">
<img class="icon" src="${static.url('images/cms-editor_text.png')}" alt="${_("Insert a text response")}">
</span>
${_("Text Input")}
</button>
</li>
<li>
@@ -46,6 +51,7 @@
<span class="problem-editor-icon number">
<img class="icon" src="${static.url('images/cms-editor_number.png')}" alt="${_("Insert a numerical response")}">
</span>
${_("Numerical Input")}
</button>
</li>
<li>
@@ -53,6 +59,7 @@
<span class="problem-editor-icon dropdown">
<img class="icon" src="${static.url('images/cms-editor_dropdown.png')}" alt="${_("Insert a dropdown response")}">
</span>
${_("Dropdown")}
</button>
</li>
<li>
@@ -60,103 +67,108 @@
<span class="problem-editor-icon explanation">
<img class="icon" src="${static.url('images/cms-editor_explanation.png')}" alt="${_("Add an explanation for this question")}">
</span>
${_("Explanation")}
</button>
</li>
</ul>
<ul class="editor-tabs">
<li><button type="button" class="xml-tab advanced-toggle" data-tab="xml">${_("Advanced Editor")}</button></li>
<li><button type="button" class="cheatsheet-toggle" data-tooltip="${_("Toggle Cheatsheet")}">?</button></li>
</ul>
</div>
<textarea class="markdown-box">${markdown | h}</textarea>
%endif
<textarea class="xml-box" rows="8" cols="40">${data | h}</textarea>
</div>
</section>
<script type="text/template" id="simple-editor-cheatsheet">
<article class="simple-editor-cheatsheet">
<div class="cheatsheet-wrapper">
<div class="row">
<h6>${_("Heading")}</h6>
<div class="col sample heading-1">
<img class="icon" src="${static.url('images/cms-editor_heading.png')}" alt="${_("Insert a heading")}">
</div>
<div class="col">
<textarea class="markdown-box">${markdown}</textarea>
<article class="simple-editor-cheatsheet shown">
<div class="cheatsheet-wrapper">
<div class="row">
<h5>${_("Markdown Help")}</h5>
</div>
<div class="row">
<div class="col sample heading-1">
<img class="icon" src="${static.url('images/cms-editor_heading.png')}" alt="${_("Insert a heading")}">
<h6>${_("Heading")}</h6>
</div>
<div class="col">
<pre><code>H3
=====
</pre>
</div>
</div>
<div class="row">
<h6>${_("Multiple Choice")}</h6>
<div class="col sample multiple-choice">
<img class="icon" src="${static.url('images/cms-editor_radio.png')}" alt="${_("Add a multiple choice question")}">
</div>
<div class="col">
</code></pre>
</div>
</div>
<div class="row">
<div class="col sample multiple-choice">
<img class="icon" src="${static.url('images/cms-editor_radio.png')}" alt="${_("Add a multiple choice question")}">
<h6>${_("Multiple Choice")}</h6>
</div>
<div class="col">
<pre><code>( ) red
( ) green
(x) blue</code></pre>
</div>
</div>
<div class="row">
<h6>${_("Checkboxes")}</h6>
<div class="col sample check-multiple">
<img class="icon" src="${static.url('images/cms-editor_checkbox.png')}" alt="${_("Add a question with checkboxes")}">
</div>
<div class="col">
</div>
</div>
<div class="row">
<div class="col sample check-multiple">
<img class="icon" src="${static.url('images/cms-editor_checkbox.png')}" alt="${_("Add a question with checkboxes")}">
<h6>${_("Checkboxes")}</h6>
</div>
<div class="col">
<pre><code>[x] earth
[ ] wind
[x] water</code></pre>
</div>
</div>
<div class="row">
<h6>${_("Text Input")}</h6>
<div class="col sample string-response">
<img class="icon" src="${static.url('images/cms-editor_text.png')}" alt="${_("Insert a text response")}">
</div>
<div class="col">
</div>
</div>
<div class="row">
<div class="col sample string-response">
<img class="icon" src="${static.url('images/cms-editor_text.png')}" alt="${_("Insert a text response")}">
<h6>${_("Text Input")}</h6>
</div>
<div class="col">
<pre><code>= dog
or= cat
or= mouse</code></pre>
</div>
</div>
<div class="row">
<div class="col sample numerical-response">
<img class="icon" src="${static.url('images/cms-editor_number.png')}" alt="${_("Insert a numerical response")}">
<h6>${_("Numerical Input")}</h6>
</div>
<div class="col">
<pre><code>= 3.14 +- 2%</code></pre>
<pre><code>= [3.14, 3.15)</code></pre>
</div>
</div>
<div class="row">
<div class="col sample option-reponse">
<img class="icon" src="${static.url('images/cms-editor_dropdown.png')}" alt="${_("Insert a dropdown response")}">
<h6>${_("Dropdown")}</h6>
</div>
<div class="col">
<pre><code>[[wrong, (right)]]</code></pre>
</div>
</div>
<div class="row">
<h6>${_("Label")}</h6>
<div class="col">
<pre><code>&gt;&gt;What is the capital of Argentina?&lt;&lt;</code></pre>
</div>
</div>
<div class="row">
<div class="col sample explanation">
<img class="icon" src="${static.url('images/cms-editor_explanation.png')}" alt="${_("Add an explanation for this question")}">
<h6>${_("Explanation")}</h6>
</div>
<div class="col">
<pre><code>[explanation] A short explanation of the answer. [explanation]</code></pre>
</div>
</div>
</div>
</div>
<div class="row">
<h6>${_("Numerical Input")}</h6>
<div class="col sample numerical-response">
<img class="icon" src="${static.url('images/cms-editor_number.png')}" alt="${_("Insert a numerical response")}">
</div>
<div class="col">
<pre><code>= 3.14 +- 2%</code></pre>
<pre><code>= [3.14, 3.15)</code></pre>
</div>
</div>
<div class="row">
<h6>${_("Dropdown")}</h6>
<div class="col sample option-reponse">
<img class="icon" src="${static.url('images/cms-editor_dropdown.png')}" alt="${_("Insert a dropdown response")}">
</div>
<div class="col">
<pre><code>[[wrong, (right)]]</code></pre>
</div>
</div>
<div class="row">
<h6>${_("Label")}</h6>
<div class="col">
<pre><code>&gt;&gt;What is the capital of Argentina?&lt;&lt;</code></pre>
</div>
</div>
<div class="row">
<h6>${_("Explanation")}</h6>
<div class="col sample explanation">
<img class="icon" src="${static.url('images/cms-editor_explanation.png')}" alt="${_("Add an explanation for this question")}">
</div>
<div class="col">
<pre><code>[explanation] A short explanation of the answer. [explanation]</code></pre>
</div>
</div>
</div>
</article>
</article>
</div>
%endif
</div>
<textarea class="xml-box" rows="8" cols="40">${data}</textarea>
</section>
<script type="text/template" id="simple-editor-cheatsheet">
</script>
</div>
<%include file="metadata-edit.html" />

View File

@@ -23,50 +23,37 @@
}
}
.cheatsheet-toggle {
width: 21px;
height: 21px;
padding: 0;
margin: -1px 5px 0 15px;
border-radius: 22px;
border: 1px solid #a5aaaf;
background: #e5ecf3;
font-size: 13px;
font-weight: 700;
color: #565d64;
text-align: center;
}
}
}
.simple-editor-cheatsheet {
position: absolute;
top: 0;
left: 100%;
top: 41px;
left: 70%;
width: 0;
border-radius: 0 3px 3px 0;
border-left: 1px solid $gray-l2;
@include linear-gradient(left, $shadow-l1, $transparent 4px);
background-color: $white;
background-color: $lightGrey;
overflow: hidden;
@include transition(width 0.3s linear 0s);
&.shown {
width: 20%;
height: 100%;
width: 30%;
height: 92%;
overflow-y: scroll;
}
.cheatsheet-wrapper {
padding: 10%;
padding: 5%;
}
h6 {
margin-top: 4px;
margin-bottom: 7px;
margin-left: 4px;
font-size: 15px;
font-weight: 700;
display: inline-block;
vertical-align: top;
}
.row {
@@ -86,7 +73,6 @@
display: block;
&.sample {
width: 60px;
margin-right: 30px;
.icon {
@@ -110,6 +96,7 @@
// adding padding to simple editor only - adjacent selector is needed since there are no toggles for CodeMirror
.markdown-box + .CodeMirror {
padding: 10px;
width: 69%;
}
}

View File

@@ -53,12 +53,6 @@
function MarkdownEditingDescriptor(element) {
var that = this;
this.toggleCheatsheetVisibility = function() {
return MarkdownEditingDescriptor.prototype.toggleCheatsheetVisibility.apply(that, arguments);
};
this.toggleCheatsheet = function() {
return MarkdownEditingDescriptor.prototype.toggleCheatsheet.apply(that, arguments);
};
this.onToolbarButton = function() {
return MarkdownEditingDescriptor.prototype.onToolbarButton.apply(that, arguments);
};
@@ -75,7 +69,6 @@
// Add listeners for toolbar buttons (only present for markdown editor)
this.element.on('click', '.xml-tab', this.onShowXMLButton);
this.element.on('click', '.format-buttons button', this.onToolbarButton);
this.element.on('click', '.cheatsheet-toggle', this.toggleCheatsheet);
// Hide the XML text area
$(this.element.find('.xml-box')).hide();
} else {
@@ -110,10 +103,6 @@
*/
MarkdownEditingDescriptor.prototype.onShowXMLButton = function(e) {
e.preventDefault();
if (this.cheatsheet && this.cheatsheet.hasClass('shown')) {
this.cheatsheet.toggleClass('shown');
this.toggleCheatsheetVisibility();
}
if (this.confirmConversionToXml()) {
this.createXMLEditor(MarkdownEditingDescriptor.markdownToXml(this.markdown_editor.getValue()));
this.xml_editor.setCursor(0);
@@ -169,29 +158,6 @@
}
};
/*
Event listener for toggling cheatsheet (only possible when markdown editor is visible).
*/
MarkdownEditingDescriptor.prototype.toggleCheatsheet = function(e) {
var that = this;
e.preventDefault();
if (!$(this.markdown_editor.getWrapperElement()).find('.simple-editor-cheatsheet')[0]) {
this.cheatsheet = $($('#simple-editor-cheatsheet').html());
$(this.markdown_editor.getWrapperElement()).append(this.cheatsheet);
}
this.toggleCheatsheetVisibility();
return setTimeout((function() {
return that.cheatsheet.toggleClass('shown');
}), 10);
};
/*
Function to toggle cheatsheet visibility.
*/
MarkdownEditingDescriptor.prototype.toggleCheatsheetVisibility = function() {
return $('.modal-content').toggleClass('cheatsheet-is-shown');
};
/*
Stores the current editor and hides the one that is not displayed.
*/
@@ -212,7 +178,6 @@
MarkdownEditingDescriptor.prototype.save = function() {
this.element.off('click', '.xml-tab', this.changeEditor);
this.element.off('click', '.format-buttons button', this.onToolbarButton);
this.element.off('click', '.cheatsheet-toggle', this.toggleCheatsheet);
if (this.current_editor === this.markdown_editor) {
return {
data: MarkdownEditingDescriptor.markdownToXml(this.markdown_editor.getValue()),
@@ -327,12 +292,13 @@
// <label>question</label> <description>description</description>
xml = xml.replace(/>>([^]+?)<</gm, function(match, questionText) {
var result = questionText.split('||'),
label = '<label>' + result[0] + '</label>\n';
label = '<label>' + result[0] + '</label>\n'; // xss-lint: disable=javascript-concat-html
// don't add empty <description> tag
if (result.length === 1 || !result[1]) {
return label;
}
// xss-lint: disable=javascript-concat-html
return label + '<description>' + result[1] + '</description>\n';
});
@@ -425,6 +391,7 @@
optiontag += correct[1];
}
optiontag += '">';
// xss-lint: disable=javascript-concat-html
return '\n<optionresponse>\n' + optiontag + '</optioninput>\n</optionresponse>\n\n';
}
@@ -442,12 +409,15 @@
if (label) {
label = ' label="' + label + '"';
}
// xss-lint: disable=javascript-concat-html
hintstr = ' <optionhint' + label + '>' + textHint.hint + '</optionhint>';
}
// xss-lint: disable=javascript-concat-html
optionlines += ' <option' + correctstr + '>' + textHint.nothint + hintstr +
'</option>\n';
}
}
// xss-lint: disable=javascript-concat-html
return '\n<optionresponse>\n <optioninput>\n' + optionlines +
' </optioninput>\n</optionresponse>\n\n';
});
@@ -477,8 +447,10 @@
hint = extractHint(value);
if (hint.hint) {
value = hint.nothint;
// xss-lint: disable=javascript-concat-html
value = value + ' <choicehint' + hint.labelassign + '>' + hint.hint + '</choicehint>';
}
// xss-lint: disable=javascript-concat-html
choices += ' <choice correct="' + correct + '"' + fixed + '>' + value + '</choice>\n';
}
}
@@ -515,6 +487,7 @@
// lone case of hint text processing outside of extractHint, since syntax here is unique
hintbody = abhint[2];
hintbody = hintbody.replace('&lf;', '\n').trim();
// xss-lint: disable=javascript-concat-html
endHints += ' <compoundhint value="' + abhint[1].trim() + '">' + hintbody +
'</compoundhint>\n';
continue; // bail
@@ -534,11 +507,13 @@
// checkbox choicehints get their own line, since there can be two of them
// <choicehint selected="true">Youre right that apple is a fruit.</choicehint>
if (select) {
// xss-lint: disable=javascript-concat-html
hints += '\n <choicehint selected="true">' + select[2].trim() +
'</choicehint>';
}
select = /{\s*(u|unselected):((.|\n)*?)}/i.exec(inner);
if (select) {
// xss-lint: disable=javascript-concat-html
hints += '\n <choicehint selected="false">' + select[2].trim() +
'</choicehint>';
}
@@ -549,6 +524,7 @@
value = hint.nothint;
}
}
// xss-lint: disable=javascript-concat-html
groupString += ' <choice correct="' + correct + '">' + value + hints + '</choice>\n';
}
}
@@ -694,10 +670,12 @@
typ = ' type="ci regexp"';
firstAnswer = firstAnswer.slice(1).trim();
}
// xss-lint: disable=javascript-concat-html
string = '<stringresponse answer="' + firstAnswer + '"' + typ + ' >\n';
if (textHint.hint) {
// xss-lint: disable=javascript-concat-html
string += ' <correcthint' + textHint.labelassign + '>' +
textHint.hint + '</correcthint>\n';
textHint.hint + '</correcthint>\n'; // xss-lint: disable=javascript-concat-html
}
// Subsequent cases are not= or or=
@@ -705,16 +683,22 @@
textHint = extractHint(values[i]);
notMatch = /^not\=\s*(.*)/.exec(textHint.nothint);
if (notMatch) {
// xss-lint: disable=javascript-concat-html
string += ' <stringequalhint answer="' + notMatch[1] + '"' +
// xss-lint: disable=javascript-concat-html
textHint.labelassign + '>' + textHint.hint + '</stringequalhint>\n';
continue;
}
orMatch = /^or\=\s*(.*)/.exec(textHint.nothint);
if (orMatch) {
// additional_answer with answer= attribute
// xss-lint: disable=javascript-concat-html
string += ' <additional_answer answer="' + orMatch[1] + '">';
if (textHint.hint) {
// xss-lint: disable=javascript-concat-html
string += '<correcthint' + textHint.labelassign + '>' +
// xss-lint: disable=javascript-concat-html
textHint.hint + '</correcthint>';
}
string += '</additional_answer>\n';
@@ -732,12 +716,15 @@
// replace explanations
xml = xml.replace(/\[explanation\]\n?([^\]]*)\[\/?explanation\]/gmi, function(match, p1) {
// xss-lint: disable=javascript-concat-html
return '<solution>\n<div class="detailed-solution">\n' +
// xss-lint: disable=javascript-concat-html
gettext('Explanation') + '\n\n' + p1 + '\n</div>\n</solution>';
});
// replace code blocks
xml = xml.replace(/\[code\]\n?([^\]]*)\[\/?code\]/gmi, function(match, p1) {
// xss-lint: disable=javascript-concat-html
return '<pre><code>' + p1 + '</code></pre>';
});

View File

@@ -28,7 +28,7 @@ class ProblemXBlockEditorView(XBlockEditorView):
"""
If editing, set the value of a field.
"""
selector = u'.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
selector = u'.metadata_edit li.field label:contains("{}") + input'.format(field_display_name)
script = "$(arguments[0]).val(arguments[1]).change();"
self.browser.execute_script(script, selector, field_value)