diff --git a/drag_and_drop_v2/drag_and_drop_v2.py b/drag_and_drop_v2/drag_and_drop_v2.py
index 21180c124..21ce146f6 100644
--- a/drag_and_drop_v2/drag_and_drop_v2.py
+++ b/drag_and_drop_v2/drag_and_drop_v2.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
#
""" Drag and Drop v2 XBlock """
+
# Imports ###########################################################
import copy
@@ -93,7 +94,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
question_text = String(
display_name=_("Problem text"),
- help=_("The description of the problem or instructions shown to the learner"),
+ help=_("The description of the problem or instructions shown to the learner."),
scope=Scope.settings,
default="",
)
@@ -106,22 +107,22 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
)
weight = Float(
- display_name=_("Weight"),
- help=_("The maximum score the learner can receive for the problem"),
+ display_name=_("Maximum score"),
+ help=_("The maximum score the learner can receive for the problem."),
scope=Scope.settings,
default=1,
)
item_background_color = String(
display_name=_("Item background color"),
- help=_("The background color of draggable items in the problem."),
+ help=_("The background color of draggable items in the problem (example: 'blue' or '#0000ff')."),
scope=Scope.settings,
default="",
)
item_text_color = String(
display_name=_("Item text color"),
- help=_("Text color to use for draggable items."),
+ help=_("Text color to use for draggable items (example: 'white' or '#ffffff')."),
scope=Scope.settings,
default="",
)
@@ -247,18 +248,17 @@ def studio_view(self, context):
"""
js_templates = loader.load_unicode('/templates/html/js_templates.html')
- help_texts = {
- field_name: self.ugettext(field.help)
- for field_name, field in self.fields.viewitems() if hasattr(field, "help")
- }
- field_values = {
- field_name: field.values
- for field_name, field in self.fields.viewitems() if hasattr(field, "values")
- }
+ # Get a 'html_id' string that is unique for this block.
+ # We append it to HTML element ID attributes to ensure multiple instances of the DnDv2 block
+ # on the same page don't share the same ID value.
+ # We avoid using ID attributes in preference to classes, but sometimes we still need IDs to
+ # connect 'for' and 'aria-describedby' attributes to the associated elements.
+ id_suffix = self.location.html_id() # pylint: disable=no-member
+ js_templates = js_templates.replace('{{id_suffix}}', id_suffix)
context = {
'js_templates': js_templates,
- 'help_texts': help_texts,
- 'field_values': field_values,
+ 'id_suffix': id_suffix,
+ 'fields': self.fields,
'self': self,
'data': urllib.quote(json.dumps(self.data)),
}
diff --git a/drag_and_drop_v2/public/css/drag_and_drop_edit.css b/drag_and_drop_v2/public/css/drag_and_drop_edit.css
index 5e1902b93..1d08961bd 100644
--- a/drag_and_drop_v2/public/css/drag_and_drop_edit.css
+++ b/drag_and_drop_v2/public/css/drag_and_drop_edit.css
@@ -61,52 +61,78 @@
position: relative;
}
-.xblock--drag-and-drop--editor .tab::after,
-.xblock--drag-and-drop--editor .tab-footer::after {
+.xblock--drag-and-drop--editor .tab::after {
content: "";
display: table;
clear: both;
}
-.xblock--drag-and-drop--editor .tab h3,
-.xblock--drag-and-drop--editor .tab .h3 {
- margin: 20px 0 8px 0;
+.xblock--drag-and-drop--editor .tab h3 {
+ font-size: 18px;
}
-.xblock--drag-and-drop--editor .tab .h3 {
+.xblock--drag-and-drop--editor .tab h4,
+.xblock--drag-and-drop--editor .tab .h4 {
display: block;
- font: inherit;
- font-size: 100%;
+ font-size: 16px;
+ margin: 20px 0 8px 0;
}
-.xblock--drag-and-drop--editor .tab .nested-input {
- display: block;
- margin-top: 8px;
+.xblock--drag-and-drop--editor .tab .items-form .item .row:first-of-type .h4 {
+ margin-top: 0;
}
.xblock--drag-and-drop--editor .tab-header,
-.xblock--drag-and-drop--editor .tab-content,
-.xblock--drag-and-drop--editor .tab-footer {
+.xblock--drag-and-drop--editor .tab-content {
width: 96%;
margin: 2%;
}
-.xblock--drag-and-drop--editor .tab-footer {
- height: 25px;
- position: relative;
- display: block;
- float: left;
-}
-
.xblock--drag-and-drop--editor .items {
width: calc(100% - 515px);
margin: 10px 0 0 0;
}
-.xblock--drag-and-drop--editor .target-image-form input,
-.xblock--drag-and-drop--editor .target-image-form textarea {
+.xblock--drag-and-drop--editor .target-image-form input {
width: 50%;
}
+.xblock--drag-and-drop--editor .target-image-form textarea {
+ width: 97%;
+}
+
+.xblock--drag-and-drop--editor .target-image-form textarea {
+ display: block;
+}
+
+.xblock--drag-and-drop--editor input,
+.xblock--drag-and-drop--editor textarea {
+ box-sizing: border-box;
+ font-size: 14px;
+ background: #fff;
+ box-shadow: none;
+ padding: 6px 8px;
+ border: 1px solid #b2b2b2;
+ color: #4c4c4c;
+}
+
+.xblock--drag-and-drop--editor label > span {
+ display: inline-block;
+ margin-bottom: 0.25em;
+}
+
+.xblock--drag-and-drop--editor label.checkbox-label {
+ font-size: 14px;
+}
+
+/* Main Tab */
+.xblock--drag-and-drop--editor .feedback-tab input,
+.xblock--drag-and-drop--editor .feedback-tab select {
+ display: block;
+}
+
+.xblock--drag-and-drop--editor .feedback-tab input[type=checkbox] {
+ display: inline-block;
+}
/* Zones Tab */
.xblock--drag-and-drop--editor .zones-tab .zone-editor {
@@ -122,7 +148,6 @@
width: 40%;
max-width: 50%;
min-width: 330px;
- margin-right: 15px;
}
.xblock--drag-and-drop--editor .zones-tab .tab-content .target {
@@ -137,39 +162,57 @@
max-width: 100%;
}
+.xblock--drag-and-drop--editor .zones-form .zone-row {
+ background-color: #b1d9f1;
+ padding: 1rem;
+ margin-bottom: 2rem;
+}
+
.xblock--drag-and-drop--editor .zones-form .zone-row label {
+ display: block;
+}
+
+.xblock--drag-and-drop--editor .zones-form .zone-row label > span {
display: inline-block;
- width: 18%;
+ font-size: 14px;
+ min-width: 8rem;
}
-.xblock--drag-and-drop--editor .zones-form .zone-row > input {
- width: 60%;
+.xblock--drag-and-drop--editor .zones-form .zone-row label > input {
+ width: 63%;
margin: 0 0 5px;
line-height: 2.664rem; /* .title gets line-height from a Studio rule that does not apply to .description;
here we make sure that both input fields get the same value for line-height */
}
-.xblock--drag-and-drop--editor .zones-form .zone-row .alignment {
- margin-bottom: 15px;
+.xblock--drag-and-drop--editor .zones-form .zone-row .layout {
+ margin: 2rem 0;
}
+.xblock--drag-and-drop--editor .zones-form .zone-row .layout label {
+ display: inline-block;
+ width: 45%;
+}
+
+.xblock--drag-and-drop--editor .zones-form .zone-row .layout .zone-size,
+.xblock--drag-and-drop--editor .zones-form .zone-row .layout .zone-coord {
+ width: 35%;
+ line-height: inherit;
+}
-.xblock--drag-and-drop--editor .zones-form .zone-row .layout .size,
-.xblock--drag-and-drop--editor .zones-form .zone-row .layout .coord {
- width: 15%;
- margin: 0 19px 5px 0;
+.xblock--drag-and-drop--editor .zones-form .zone-row .alignment {
+ margin-bottom: 15px;
}
+
.xblock--drag-and-drop--editor .feedback-form textarea {
width: 99%;
height: 128px;
}
-.xblock--drag-and-drop--editor .zones-form .zones-form-help,
-.xblock--drag-and-drop--editor .target-image-form .target-image-form-help,
-.xblock--drag-and-drop--editor .item-styles-form .item-styles-form-help {
- margin-top: 5px;
- font-size: small;
+.xblock--drag-and-drop--editor .form-help {
+ margin: 0.5rem 0 1rem;
+ font-size: 12px;
}
.xblock--drag-and-drop--editor .item-styles-form,
@@ -178,14 +221,9 @@
}
.xblock--drag-and-drop--editor .items-form .item {
- background-color: #8fcaec;
- padding: 10px 0 1px;
- margin: 15px 0;
-}
-
-.xblock--drag-and-drop--editor .items-form label,
-.xblock--drag-and-drop--editor .items-form .h3 {
- margin: 0 1%;
+ background-color: #b1d9f1;
+ padding: 2rem;
+ margin-bottom: 2rem;
}
.xblock--drag-and-drop--editor .items-form select {
@@ -199,7 +237,6 @@
.xblock--drag-and-drop--editor .items-form .item-text,
.xblock--drag-and-drop--editor .items-form .item-image-url {
width: 50%;
- margin: 0 1%;
}
.xblock--drag-and-drop--editor .items-form .item-width {
@@ -208,23 +245,30 @@
.xblock--drag-and-drop--editor .items-form textarea {
width: 97%;
- margin: 0 1%;
}
-.xblock--drag-and-drop--editor .items-form .zone-checkbox-label {
- text-align: left;
-}
-
-.xblock--drag-and-drop--editor .items-form .row {
- margin-bottom: 20px;
-}
.xblock--drag-and-drop--editor .items-form .row.advanced {
display: none;
}
.xblock--drag-and-drop--editor .items-form .row.advanced-link {
- padding-left: 1em;
- font-size: 80%;
+ font-size: 12px;
+ margin-top: 2em;
+}
+.xblock--drag-and-drop--editor .items-form .row.advanced-link button {
+ background: none;
+ border: none;
+ color: #4c4c4c;
+}
+.xblock--drag-and-drop--editor .items-form .row.advanced-link button:before {
+ content: '\25B6';
+ margin-right: 0.5em;
}
+.rtl .xblock--drag-and-drop--editor .items-form .row.advanced-link button:before {
+ content: '\25C0';
+ margin-left: 0.5em;
+ margin-right: 0;
+}
+
.xblock--drag-and-drop-editor .items-form .zone-checkbox-row {
margin-bottom: 0px;
}
@@ -238,48 +282,33 @@
padding: 5px 10px;
}
-.xblock--drag-and-drop--editor .btn:hover {
- opacity: 0.8;
- cursor: pointer;
-}
-
+.xblock--drag-and-drop--editor .btn:hover,
.xblock--drag-and-drop--editor .btn:focus {
- outline: none;
- opacity: 0.5;
-}
-
-.xblock--drag-and-drop--editor .add-element {
- text-decoration: none;
- color: #1d5280;
-}
-
-.xblock--drag-and-drop--editor .remove-zone {
- float: right;
- margin-top: 2px;
- margin-right: 16px;
+ background-color: #296ba5;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.5);
}
+.xblock--drag-and-drop--editor .remove-zone,
.xblock--drag-and-drop--editor .remove-item {
float: right;
- display: inline-block;
- margin-right: 16px;
+ padding: 3px;
+ border-radius: 12px;
+}
+.rtl .xblock--drag-and-drop--editor .remove-zone,
+.rtl .xblock--drag-and-drop--editor .remove-item {
+ float: left;
}
.xblock--drag-and-drop--editor .icon {
width: 14px;
height: 14px;
border-radius: 7px;
- background-color: #1d5280;
- position: relative;
- float: left;
- margin: 0 5px 0 0;
+ display: inline-block;
}
-.xblock--drag-and-drop--editor .add-zone:hover,
-.xblock--drag-and-drop--editor .add-zone:hover .icon,
-.xblock--drag-and-drop--editor .remove-zone:hover,
-.xblock--drag-and-drop--editor .remove-zone:hover .icon {
- opacity: 0.7;
+.xblock--drag-and-drop--editor .remove-zone .icon,
+.xblock--drag-and-drop--editor .remove-item .icon {
+ display: block;
}
.xblock--drag-and-drop--editor .tab .field-error {
@@ -342,12 +371,3 @@
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
-
-.xblock--drag-and-drop--editor .remove-item .icon.remove {
- background-color: #fff;
- color: #0072a7; /* Override default color from Studio to ensure contrast is large enough */
-}
-.xblock--drag-and-drop--editor .remove-item .icon.remove:before,
-.xblock--drag-and-drop--editor .remove-item .icon.remove:after {
- background-color: #1d5280;
-}
diff --git a/drag_and_drop_v2/public/js/drag_and_drop_edit.js b/drag_and_drop_v2/public/js/drag_and_drop_edit.js
index a9c5ed63f..af7161fb1 100644
--- a/drag_and_drop_v2/public/js/drag_and_drop_edit.js
+++ b/drag_and_drop_v2/public/js/drag_and_drop_edit.js
@@ -29,10 +29,10 @@ function DragAndDropEditBlock(runtime, element, params) {
tpl: {
init: function() {
_fn.tpl = {
- zoneInput: Handlebars.compile($("#zone-input-tpl", element).html()),
- zoneElement: Handlebars.compile($("#zone-element-tpl", element).html()),
- zoneCheckbox: Handlebars.compile($("#zone-checkbox-tpl", element).html()),
- itemInput: Handlebars.compile($("#item-input-tpl", element).html()),
+ zoneInput: Handlebars.compile($(".zone-input-tpl", element).html()),
+ zoneElement: Handlebars.compile($(".zone-element-tpl", element).html()),
+ zoneCheckbox: Handlebars.compile($(".zone-checkbox-tpl", element).html()),
+ itemInput: Handlebars.compile($(".item-input-tpl", element).html()),
};
}
},
@@ -66,7 +66,10 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn.build.clickHandlers();
// Hide settings that are specific to assessment mode
- _fn.build.$el.feedback.form.find('#problem-mode').trigger('change');
+ _fn.build.$el.feedback.form.find('.problem-mode').trigger('change');
+
+ // Set focus on first input field.
+ $element.find('input:first').select();
},
validate: function() {
@@ -120,8 +123,8 @@ function DragAndDropEditBlock(runtime, element, params) {
}
// Set the target image and bind its event handler:
- $('.target-image-form #background-url', element).val(_fn.data.targetImg);
- $('.target-image-form #background-description', element).val(_fn.data.targetImgDescription);
+ $('.target-image-form .background-url', element).val(_fn.data.targetImg);
+ $('.target-image-form .background-description', element).val(_fn.data.targetImgDescription);
_fn.build.$el.targetImage.load(_fn.build.form.zone.imageLoaded);
_fn.build.$el.targetImage.attr('src', params.target_img_expanded_url);
_fn.build.$el.targetImage.attr('alt', _fn.data.targetImgDescription);
@@ -137,6 +140,7 @@ function DragAndDropEditBlock(runtime, element, params) {
$fbkTab.addClass('hidden');
$zoneTab.removeClass('hidden');
self.scrollToTop();
+ $zoneTab.find('input:first').select();
$(this).one('click', function loadThirdTab(e) {
// $zoneTab -> $itemTab
@@ -157,6 +161,7 @@ function DragAndDropEditBlock(runtime, element, params) {
$zoneTab.addClass('hidden');
$itemTab.removeClass('hidden');
self.scrollToTop();
+ $itemTab.find('input:first').select();
$(this).addClass('hidden');
$('.save-button', element).parent()
@@ -175,20 +180,19 @@ function DragAndDropEditBlock(runtime, element, params) {
});
$fbkTab
- .on('change', '#problem-mode', _fn.build.form.problem.toggleAssessmentSettings);
+ .on('change', '.problem-mode', _fn.build.form.problem.toggleAssessmentSettings);
$zoneTab
.on('click', '.add-zone', function(e) {
- e.preventDefault();
_fn.build.form.zone.add();
+ // Set focus to first field of the new zone.
+ $('.zones-form .zone-row:last input[type=text]:first', element).select();
})
.on('click', '.remove-zone', _fn.build.form.zone.remove)
.on('input', '.zone-row input', _fn.build.form.zone.changedInputHandler)
.on('change', '.align-select', _fn.build.form.zone.changedInputHandler)
.on('click', '.target-image-form button', function(e) {
- e.preventDefault();
-
- var new_img_url = $.trim($('.target-image-form #background-url', element).val());
+ var new_img_url = $.trim($('.target-image-form .background-url', element).val());
if (new_img_url) {
// We may need to 'expand' the URL before it will be valid.
// e.g. '/static/blah.png' becomes '/asset-v1:course+id/blah.png'
@@ -202,9 +206,9 @@ function DragAndDropEditBlock(runtime, element, params) {
}
_fn.data.targetImg = new_img_url;
})
- .on('input', '.target-image-form #background-description', function(e) {
+ .on('input', '.target-image-form .background-description', function(e) {
var new_description = $.trim(
- $('.target-image-form #background-description', element).val()
+ $('.target-image-form .background-description', element).val()
);
_fn.build.$el.targetImage.attr('alt', new_description);
_fn.data.targetImgDescription = new_description;
@@ -218,11 +222,12 @@ function DragAndDropEditBlock(runtime, element, params) {
$itemTab
.on('click', '.add-item', function(e) {
- e.preventDefault();
_fn.build.form.item.add();
+ // Set focus to first field of the new item.
+ $('.items-form .item:last input[type=text]:first', element).select();
})
.on('click', '.remove-item', _fn.build.form.item.remove)
- .on('click', '.advanced-link a', _fn.build.form.item.showAdvancedSettings)
+ .on('click', '.advanced-link button', _fn.build.form.item.showAdvancedSettings)
.on('input', '.item-image-url', _fn.build.form.item.imageURLChanged);
},
form: {
@@ -230,8 +235,8 @@ function DragAndDropEditBlock(runtime, element, params) {
toggleAssessmentSettings: function(e) {
e.preventDefault();
var $modeSetting = $(e.currentTarget),
- $problemForm = $modeSetting.parent('form'),
- $assessmentSettings = $problemForm.find('.setting.assessment');
+ $problemForm = $modeSetting.closest('form'),
+ $assessmentSettings = $problemForm.find('.assessment-setting');
if ($modeSetting.val() === 'assessment') {
$assessmentSettings.show();
} else {
@@ -299,7 +304,6 @@ function DragAndDropEditBlock(runtime, element, params) {
uid = String($el.data('uid')), // cast to string since UID must be string but .data() converts data-uid="5" to 5
array_index;
- e.preventDefault();
$el.detach();
// Find the uid of the zone in the array and remove it.
@@ -354,19 +358,19 @@ function DragAndDropEditBlock(runtime, element, params) {
var $changedInput = $(ev.currentTarget);
var $row = $changedInput.closest('.zone-row');
var record = _fn.build.form.zone.getZoneObjByUID(String($row.data('uid')));
- if ($changedInput.hasClass('title')) {
+ if ($changedInput.hasClass('zone-title')) {
record.title = $changedInput.val();
- } else if ($changedInput.hasClass('width')) {
+ } else if ($changedInput.hasClass('zone-width')) {
record.width = $changedInput.val();
- } else if ($changedInput.hasClass('description')) {
+ } else if ($changedInput.hasClass('zone-description')) {
record.description = $changedInput.val();
- } else if ($changedInput.hasClass('height')) {
+ } else if ($changedInput.hasClass('zone-height')) {
record.height = $changedInput.val();
- } else if ($changedInput.hasClass('x')) {
+ } else if ($changedInput.hasClass('zone-x')) {
record.x = $changedInput.val();
- } else if ($changedInput.hasClass('y')) {
+ } else if ($changedInput.hasClass('zone-y')) {
record.y = $changedInput.val();
- } else if ($changedInput.hasClass('align-select')) {
+ } else if ($changedInput.hasClass('zone-align-select')) {
record.align = $changedInput.val();
}
_fn.build.form.zone.renderZonesPreview();
@@ -394,8 +398,8 @@ function DragAndDropEditBlock(runtime, element, params) {
},
feedback: function($form) {
_fn.data.feedback = {
- start: $form.find('#intro-feedback').val(),
- finish: $form.find('#final-feedback').val()
+ start: $form.find('.intro-feedback').val(),
+ finish: $form.find('.final-feedback').val()
};
},
item: {
@@ -430,7 +434,7 @@ function DragAndDropEditBlock(runtime, element, params) {
}
ctx.checkboxes = _fn.build.form.createCheckboxes(ctx.zones);
- _fn.build.form.item.count++;
+ ctx.index = _fn.build.form.item.count++;
$form.append(tpl(ctx));
_fn.build.form.item.enableDelete();
@@ -438,7 +442,6 @@ function DragAndDropEditBlock(runtime, element, params) {
remove: function(e) {
var $el = $(e.currentTarget).closest('.item');
- e.preventDefault();
$el.detach();
_fn.build.form.item.count--;
@@ -462,7 +465,6 @@ function DragAndDropEditBlock(runtime, element, params) {
}
},
showAdvancedSettings: function(e) {
- e.preventDefault();
var $el = $(e.currentTarget).closest('.item');
$el.find('.row.advanced').show();
$el.find('.row.advanced-link').hide();
@@ -505,15 +507,15 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn.data.zones = _fn.build.form.zone.zoneObjects;
var data = {
- 'display_name': $element.find('#display-name').val(),
- 'mode': $element.find("#problem-mode").val(),
+ 'display_name': $element.find('.display-name').val(),
+ 'mode': $element.find(".problem-mode").val(),
'max_attempts': $element.find(".max-attempts").val(),
'show_title': $element.find('.show-title').is(':checked'),
- 'weight': $element.find('#weight').val(),
- 'problem_text': $element.find('#problem-text').val(),
+ 'weight': $element.find('.weight').val(),
+ 'problem_text': $element.find('.problem-text').val(),
'show_problem_header': $element.find('.show-problem-header').is(':checked'),
- 'item_background_color': $element.find('#item-background-color').val(),
- 'item_text_color': $element.find('#item-text-color').val(),
+ 'item_background_color': $element.find('.item-background-color').val(),
+ 'item_text_color': $element.find('.item-text-color').val(),
'data': _fn.data,
};
diff --git a/drag_and_drop_v2/templates/html/drag_and_drop_edit.html b/drag_and_drop_v2/templates/html/drag_and_drop_edit.html
index 6e0fa1876..7836892fe 100644
--- a/drag_and_drop_v2/templates/html/drag_and_drop_edit.html
+++ b/drag_and_drop_v2/templates/html/drag_and_drop_edit.html
@@ -12,51 +12,73 @@