Commit e8b10924 by Tim Krones

Make DnDv2 accessible.

parent c90e8579
......@@ -153,6 +153,7 @@ class DragAndDropBlock(XBlock):
"question_text": self.question_text,
"show_question_header": self.show_question_header,
"target_img_expanded_url": self.target_img_expanded_url,
"target_img_description": self.target_img_description,
"item_background_color": self.item_background_color or None,
"item_text_color": self.item_text_color or None,
"initial_feedback": self.data['feedback']['start'],
......@@ -197,6 +198,7 @@ class DragAndDropBlock(XBlock):
fragment.initialize_js('DragAndDropEditBlock', {
'data': self.data,
'target_img_expanded_url': self.target_img_expanded_url,
'target_img_description': self.target_img_description,
'default_background_image_url': self.default_background_image_url,
})
......@@ -329,6 +331,11 @@ class DragAndDropBlock(XBlock):
return self.default_background_image_url
@property
def target_img_description(self):
""" Get the description for the target image (the image items are dragged onto). """
return self.data.get("targetImgDescription", "")
@property
def default_background_image_url(self):
""" The URL to the default background image, shown when no custom background is used """
return self.runtime.local_resource_url(self, "public/img/triangle.png")
......
......@@ -23,7 +23,7 @@
/* Shared styles used in header and footer */
.xblock--drag-and-drop .title1 {
color: rgb(85, 85, 85);
color: #555555;
text-transform: uppercase;
font-weight: bold;
font-style: normal;
......@@ -58,7 +58,7 @@
align-items: center;
position: relative;
border: 1px solid rgba(0,0,0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 3px;
padding: 5px;
}
......@@ -72,7 +72,7 @@
border-radius: 3px;
margin: 5px;
padding: 10px;
background-color: #2e83cd;
background-color: #2872b2;
font-size: 14px;
color: #fff;
opacity: 1;
......@@ -112,8 +112,14 @@
transform: translate(-50%, -50%); /* These blocks are to be centered on their absolute x,y position */
}
/* Focused option */
.xblock--drag-and-drop .drag-container .item-bank .option:focus,
.xblock--drag-and-drop .drag-container .item-bank .option:hover {
transform: scale(1.1);
}
.xblock--drag-and-drop .drag-container .ui-draggable-dragging {
box-shadow: 0 16px 32px 0 rgba(0,0,0,.3);
box-shadow: 0 16px 32px 0 rgba(0, 0, 0, 0.3);
border: 1px solid #ccc;
opacity: .65;
z-index: 20 !important;
......@@ -159,7 +165,7 @@
.xblock--drag-and-drop .drag-container .option .numerical-input.correct .input {
background: #ceffce;
color: #0dad0d;
color: #087108;
}
.xblock--drag-and-drop .drag-container .option .numerical-input.incorrect .input {
......@@ -224,8 +230,12 @@
}
/* Focused zone */
.xblock--drag-and-drop .zone:focus {
border: 2px solid #a5a5a5;
}
.xblock--drag-and-drop .zone p {
visibility: hidden;
width: 100%;
font-family: Arial;
font-size: 16px;
......@@ -250,7 +260,7 @@
/*** FEEDBACK ***/
.xblock--drag-and-drop .feedback {
margin-top: 20px;
border-top: solid 1px rgb(189, 189, 189);
border-top: solid 1px #bdbdbd;
}
.xblock--drag-and-drop .popup {
......@@ -258,8 +268,8 @@
display: none;
top: 5%;
right: 5%;
border: 1px solid white;
background: none repeat scroll 0 0 rgba(0,0,0,.8);
border: 1px solid #fff;
background: none repeat scroll 0 0 rgba(0, 0, 0, 0.8);
width: 500px;
max-width: 90%;
min-height: 50px;
......@@ -269,7 +279,7 @@
}
.xblock--drag-and-drop .popup .popup-content {
color: #FFFFFF;
color: #ffffff;
margin-left: 15px;
margin-top: 35px;
margin-bottom: 15px;
......@@ -282,7 +292,7 @@
margin-right: 8px;
margin-top: 8px;
margin-left: 20px;
color: #FFFFFF;
color: #ffffff;
font-family: "fontawesome";
font-size: 18pt;
}
......@@ -290,6 +300,20 @@
.xblock--drag-and-drop .reset-button {
cursor: pointer;
float: right;
color: #3384CA;
color: #2d74b3;
margin-top: 3px;
}
/* Make sure screen-reader content is hidden in the workbench: */
.xblock--drag-and-drop .sr {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
background: #ffffff;
color: #000000;
}
......@@ -141,9 +141,12 @@
width: 18%;
}
.xblock--drag-and-drop--editor .zones-form .zone-row .title {
.xblock--drag-and-drop--editor .zones-form .zone-row .title,
.xblock--drag-and-drop--editor .zones-form .zone-row .description {
width: 60%;
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 .layout {
......@@ -162,6 +165,7 @@
height: 128px;
}
.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;
......@@ -173,7 +177,7 @@
}
.xblock--drag-and-drop--editor .items-form .item {
background: #73bde7;
background: #8fcaec;
padding: 10px 0 1px;
margin: 15px 0;
}
......@@ -209,11 +213,12 @@
/** Buttons **/
.xblock--drag-and-drop--editor .btn {
background: #2e83cd;
background: #2872b2;
color: #fff;
border: 1px solid #156ab4;
border-radius: 6px;
padding: 5px 10px;
margin-top: 15px;
}
.xblock--drag-and-drop--editor .btn:hover {
......@@ -228,7 +233,7 @@
.xblock--drag-and-drop--editor .add-element {
text-decoration: none;
color: #2e83cd;
color: #236299;
}
.xblock--drag-and-drop--editor .remove-zone {
......@@ -246,7 +251,7 @@
width: 14px;
height: 14px;
border-radius: 7px;
background: #2e83cd;
background: #236299;
position: relative;
float: left;
margin: 0 5px 0 0;
......@@ -315,8 +320,9 @@
.xblock--drag-and-drop--editor .remove-item .icon.remove {
background: #fff;
color: #0072a7;
}
.xblock--drag-and-drop--editor .remove-item .icon.remove:before,
.xblock--drag-and-drop--editor .remove-item .icon.remove:after {
background: #2e83cd;
background: #236299;
}
......@@ -52,10 +52,11 @@ function DragAndDropBlock(runtime, element, configuration) {
promise.reject();
}
}, false);
img.addEventListener("error", function() { promise.reject() });
img.addEventListener("error", function() { promise.reject(); });
img.src = configuration.target_img_expanded_url;
img.alt = configuration.target_img_description;
return promise;
}
};
/** Zones are specified in the configuration via pixel values - convert to percentages */
var computeZoneDimension = function(zone, bg_image_width, bg_image_height) {
......@@ -310,13 +311,17 @@ function DragAndDropBlock(runtime, element, configuration) {
input.class_name = item_user_state.correct_input ? 'correct' : 'incorrect';
}
}
var content_html = item.displayName;
if (item.backgroundImage) {
content_html = '<img src="' + item.backgroundImage + '" alt="' + item.backgroundDescription + '" />';
}
var itemProperties = {
value: item.id,
drag_disabled: Boolean(item_user_state || state.finished),
class_name: item_user_state && ('input' in item_user_state || item_user_state.correct_input) ? 'fade': undefined,
xhr_active: (item_user_state && item_user_state.submitting_location),
input: input,
content_html: item.backgroundImage ? '<img src="' + item.backgroundImage + '"/>' : item.displayName,
content_html: content_html,
has_image: !!item.backgroundImage
};
if (item_user_state) {
......@@ -340,6 +345,7 @@ function DragAndDropBlock(runtime, element, configuration) {
question_html: configuration.question_text,
show_question_header: configuration.show_question_header,
target_img_src: configuration.target_img_expanded_url,
target_img_description: configuration.target_img_description,
display_zone_labels: configuration.display_zone_labels,
zones: configuration.zones,
items: items,
......
......@@ -70,9 +70,11 @@ function DragAndDropEditBlock(runtime, element, params) {
}
// Set the target image and bind its event handler:
$('.target-image-form input', element).val(_fn.data.targetImg);
$('.target-image-form input.url-input', element).val(_fn.data.targetImg);
$('.target-image-form input.description-input', 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', params.target_img_description);
if (_fn.data.displayLabels) {
$('.display-labels-form input', element).prop('checked', true);
......@@ -122,7 +124,7 @@ function DragAndDropEditBlock(runtime, element, params) {
.on('click', '.target-image-form button', function(e) {
e.preventDefault();
var new_img_url = $.trim($('.target-image-form input', element).val());
var new_img_url = $.trim($('.target-image-form input.url-input', 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'
......@@ -136,6 +138,12 @@ function DragAndDropEditBlock(runtime, element, params) {
}
_fn.data.targetImg = new_img_url;
var new_description = $.trim(
$('.target-image-form input.description-input', element).val()
);
_fn.build.$el.targetImage.attr('alt', new_description);
_fn.data.targetImgDescription = new_description;
// Placeholder shim for IE9
$.placeholder.shim();
})
......@@ -176,6 +184,7 @@ function DragAndDropEditBlock(runtime, element, params) {
// Update zone obj
var zoneObj = {
title: oldZone.title || 'Zone ' + num,
description: oldZone.description,
id: name,
index: num,
width: oldZone.width || 200,
......@@ -247,6 +256,7 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn.tpl.zoneElement({
id: zoneObj.id,
title: zoneObj.title,
description: zoneObj.description,
x_percent: (+zoneObj.x) / imgWidth * 100,
y_percent: (+zoneObj.y) / imgHeight * 100,
width_percent: (+zoneObj.width) / imgWidth * 100,
......@@ -276,6 +286,8 @@ function DragAndDropEditBlock(runtime, element, params) {
record.title = $changedInput.val();
} else if ($changedInput.hasClass('width')) {
record.width = $changedInput.val();
} else if ($changedInput.hasClass('description')) {
record.description = $changedInput.val();
} else if ($changedInput.hasClass('height')) {
record.height = $changedInput.val();
} else if ($changedInput.hasClass('x')) {
......@@ -376,7 +388,8 @@ function DragAndDropEditBlock(runtime, element, params) {
$form.each(function(i, el) {
var $el = $(el),
name = $el.find('.item-text').val(),
backgroundImage = $el.find('.background-image').val();
backgroundImage = $el.find('.background-image-url').val(),
backgroundDescription = $el.find('.background-image-description').val();
if (name.length > 0 || backgroundImage.length > 0) {
// Item width/height are ignored, but preserve the data:
......@@ -401,7 +414,8 @@ function DragAndDropEditBlock(runtime, element, params) {
width: width,
height: height
},
backgroundImage: backgroundImage
backgroundImage: backgroundImage,
backgroundDescription: backgroundDescription
};
var numValue = parseFloat($el.find('.item-numerical-value').val());
......@@ -410,7 +424,7 @@ function DragAndDropEditBlock(runtime, element, params) {
data.inputOptions = {
value: numValue,
margin: isFinite(numMargin) ? numMargin : 0
}
};
}
items.push(data);
......
......@@ -55,6 +55,7 @@
var itemTemplate = function(item) {
var style = {};
var className = (item.class_name) ? item.class_name : "";
var tabindex = 0;
if (item.background_color) {
style['background-color'] = item.background_color;
}
......@@ -64,6 +65,8 @@
if (item.is_placed) {
style.left = item.x_percent + "%";
style.top = item.y_percent + "%";
tabindex = -1; // If an item has been placed it can no longer be interacted with,
// so remove the ability to move focus to it using the keyboard
}
if (item.has_image) {
className += " " + "option-with-image";
......@@ -73,7 +76,13 @@
{
key: item.value,
className: className,
attributes: {'data-value': item.value, 'data-drag-disabled': item.drag_disabled},
attributes: {
'tabindex': tabindex,
'draggable': true,
'aria-grabbed': false,
'data-value': item.value,
'data-drag-disabled': item.drag_disabled
},
style: style
}, [
itemSpinnerTemplate(item.xhr_active),
......@@ -85,18 +94,27 @@
};
var zoneTemplate = function(zone, ctx) {
var className = ctx.display_zone_labels ? 'zone-name' : 'zone-name sr';
return (
h(
'div.zone',
{
id: zone.id,
attributes: {'data-zone': zone.title},
attributes: {
'tabindex': 0,
'dropzone': 'move',
'aria-dropeffect': 'move',
'data-zone': zone.title
},
style: {
top: zone.y_percent + '%', left: zone.x_percent + "%",
width: zone.width_percent + '%', height: zone.height_percent + "%",
}
},
ctx.display_zone_labels ? h('p', zone.title) : null
[
h('p', { className: className }, zone.title),
h('p', { className: 'zone-description sr' }, zone.description)
]
)
);
};
......@@ -104,8 +122,9 @@
var feedbackTemplate = function(ctx) {
var feedback_display = ctx.feedback_html ? 'block' : 'none';
var reset_button_display = ctx.display_reset_button ? 'block' : 'none';
var properties = { attributes: { 'aria-live': 'polite' } };
return (
h('section.feedback', [
h('section.feedback', properties, [
h('div.reset-button', {style: {display: reset_button_display}}, gettext('Reset exercise')),
h('h3.title1', {style: {display: feedback_display}}, gettext('Feedback')),
h('p.message', {style: {display: feedback_display},
......@@ -135,7 +154,7 @@
h('p.popup-content', {innerHTML: ctx.popup_html}),
]),
h('div.target-img-wrapper', [
h('img.target-img', {src: ctx.target_img_src, alt: "Image Description here"}),
h('img.target-img', {src: ctx.target_img_src, alt: ctx.target_img_description}),
]),
renderCollection(zoneTemplate, ctx.zones, ctx),
renderCollection(itemTemplate, items_placed, ctx),
......
......@@ -41,18 +41,39 @@
<div class="tab zones-tab hidden">
<header class="tab-header">
<h3>{% trans "Zone Positions" %}</h3>
<h3>{% trans "Zones" %}</h3>
</header>
<section class="tab-content">
<section class="tab-content target-image-form">
<label>{% trans "New background URL" %}:</label>
<input type="text">
<form class="target-image-form">
<h3 id="background-url-label">
{% trans "Background URL" %}
</h3>
<input type="text" class="url-input" aria-labelledby="background-url-label">
<h3 id="background-description-label">
{% trans "Background description" %}
</h3>
<input type="text"
class="description-input"
aria-labelledby="background-description-label"
aria-describedby="background-description-description">
<div id="background-description-description" class="target-image-form-help">
{% blocktrans %}
Please provide a description of the image for non-visual users.
The description should provide sufficient information that would allow anyone
to solve the problem if the image did not load.
{% endblocktrans %}
</div>
<button class="btn">{% trans "Change background" %}</button>
</form>
</section>
<section class="tab-content display-labels-form">
<section class="tab-content">
<form class="display-labels-form">
<h3>{% trans "Zone labels" %}</h3>
<label for="display-labels">{% trans "Display label names on the image" %}:</label>
<input name="display-labels" id="display-labels" type="checkbox" />
</form>
</section>
<section class="tab-content">
<div class="zone-editor">
<div class="controls">
<form class="zones-form"></form>
......
......@@ -5,6 +5,7 @@
width:{{ width_percent }}%;
height:{{ height_percent }}%;">
<p>{{{ title }}}</p>
<p class="sr">{{{ description }}}</p>
</div>
</script>
......@@ -15,6 +16,11 @@
<a href="#" class="remove-zone hidden">
<div class="icon remove"></div>
</a>
<label>{{i18n "Description"}}</label>
<input type="text"
class="description"
value="{{ description }}"
placeholder="{{i18n 'Describe this zone to non-visual users'}}" />
<div class="layout">
<label>{{i18n "width"}}</label>
<input type="text" class="size width" value="{{ width }}" />
......@@ -46,7 +52,11 @@
</div>
<div class="row">
<label>{{i18n "Background image URL (alternative to the text)"}}</label>
<textarea class="background-image">{{ backgroundImage }}</textarea>
<textarea class="background-image-url">{{ backgroundImage }}</textarea>
</div>
<div class="row">
<label>{{i18n "Background image description (should provide sufficient information to place the item even if the image did not load)"}}</label>
<textarea class="background-image-description">{{ backgroundDescription }}</textarea>
</div>
<div class="row">
<label>{{i18n "Success Feedback"}}</label>
......
{
"zones": [
{
"index": 1,
"title": "Zone 1",
"description": "This describes zone 1",
"height": 178,
"width": 196,
"y": "30",
"x": "160",
"id": "zone-1"
},
{
"index": 2,
"title": "Zone 2",
"description": "This describes zone 2",
"height": 140,
"width": 340,
"y": "210",
"x": "86",
"id": "zone-2"
}
],
"items": [
{
"displayName": "1",
"backgroundImage": "https://placehold.it/100x100",
"backgroundDescription": "This describes the background image of item 1",
"feedback": {
"incorrect": "No, 1 does not belong here",
"correct": "Yes, 1 goes here"
},
"zone": "Zone 1",
"id": 0
},
{
"displayName": "2",
"backgroundImage": "https://placehold.it/100x100",
"backgroundDescription": "This describes the background image of item 2",
"feedback": {
"incorrect": "No, 2 does not belong here",
"correct": "Yes, 2 goes here"
},
"zone": "Zone 2",
"id": 1
},
{
"displayName": "X",
"backgroundImage": "",
"feedback": {
"incorrect": "You silly, there are no zones for X",
"correct": ""
},
"zone": "none",
"id": 2
}
],
"feedback": {
"start": "Drag the items onto the image above.",
"finish": "Good work! You have completed this drag and drop exercise."
},
"targetImgDescription": "This describes the target image"
}
......@@ -59,5 +59,6 @@
"start": "Intro <i>Feed</i>",
"finish": "Final <b>Feed</b>"
},
"targetImg": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDAiIGhlaWdodD0iNjAwIiBzdHlsZT0iYmFja2dyb3VuZDogI2VlZjsiPjwvc3ZnPg=="
"targetImg": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDAiIGhlaWdodD0iNjAwIiBzdHlsZT0iYmFja2dyb3VuZDogI2VlZjsiPjwvc3ZnPg==",
"targetImgDescription": "This describes the target image"
}
......@@ -57,6 +57,9 @@ class BaseIntegrationTest(SeleniumBaseTest):
def _get_zones(self):
return self._page.find_elements_by_css_selector(".drag-container .zone")
def _get_feedback(self):
return self._page.find_element_by_css_selector(".feedback")
def _get_feedback_message(self):
return self._page.find_element_by_css_selector(".feedback .message")
......
......@@ -31,4 +31,6 @@ class TestCustomDataDragAndDropRendering(BaseIntegrationTest):
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciI"
"HdpZHRoPSI4MDAiIGhlaWdodD0iNjAwIiBzdHlsZT0iYmFja2dyb3VuZDogI2VlZjsiPjwvc3ZnPg=="
)
custom_image_description = "This describes the target image"
self.assertEqual(bg_image.get_attribute("src"), custom_image_url)
self.assertEqual(bg_image.get_attribute("alt"), custom_image_description)
......@@ -145,7 +145,7 @@ class InteractionTestBase(object):
self.assertDictEqual(locations_after_reset[item_key], initial_locations[item_key])
class InteractionTestFixture(InteractionTestBase):
class BasicInteractionTest(InteractionTestBase):
"""
Verifying Drag and Drop XBlock rendering against default data - if default data changes this will probably break.
"""
......@@ -184,7 +184,7 @@ class InteractionTestFixture(InteractionTestBase):
self.parameterized_final_feedback_and_reset(self.items_map, self.feedback)
class CustomDataInteractionTest(InteractionTestFixture, BaseIntegrationTest):
class CustomDataInteractionTest(BasicInteractionTest, BaseIntegrationTest):
items_map = {
0: ItemDefinition(0, 'Zone 1', "Yes 1", "No 1"),
1: ItemDefinition(1, 'Zone 2', "Yes 2", "No 2", "102"),
......@@ -202,7 +202,7 @@ class CustomDataInteractionTest(InteractionTestFixture, BaseIntegrationTest):
return self._get_custom_scenario_xml("integration/data/test_data.json")
class CustomHtmlDataInteractionTest(InteractionTestFixture, BaseIntegrationTest):
class CustomHtmlDataInteractionTest(BasicInteractionTest, BaseIntegrationTest):
items_map = {
0: ItemDefinition(0, 'Zone <i>1</i>', "Yes <b>1</b>", "No <b>1</b>"),
1: ItemDefinition(1, 'Zone <b>2</b>', "Yes <i>2</i>", "No <i>2</i>", "95"),
......
from ddt import ddt, unpack, data
from selenium.common.exceptions import NoSuchElementException
from ..utils import load_resource
from .test_base import BaseIntegrationTest
class Colors(object):
WHITE = 'rgb(255, 255, 255)'
BLUE = 'rgb(46, 131, 205)'
BLUE = 'rgb(40, 114, 178)'
GREY = 'rgb(237, 237, 237)'
CORAL = '#ff7f50'
CORNFLOWERBLUE = 'cornflowerblue'
......@@ -32,10 +34,16 @@ class TestDragAndDropRender(BaseIntegrationTest):
def load_scenario(self, item_background_color="", item_text_color=""):
scenario_xml = """
<vertical_demo>
<drag-and-drop-v2 item_background_color='{item_background_color}' item_text_color='{item_text_color}' />
</vertical_demo>
""".format(item_background_color=item_background_color, item_text_color=item_text_color)
<vertical_demo>
<drag-and-drop-v2 item_background_color='{item_background_color}'
item_text_color='{item_text_color}'
data='{data}' />
</vertical_demo>
""".format(
item_background_color=item_background_color,
item_text_color=item_text_color,
data=load_resource("integration/data/test_data_a11y.json")
)
self._add_scenario(self.PAGE_ID, self.PAGE_TITLE, scenario_xml)
self.browser.get(self.live_server_url)
......@@ -83,18 +91,9 @@ class TestDragAndDropRender(BaseIntegrationTest):
color = self._get_style('.item-bank .option[data-value='+item_val+']', 'color')
self.assertEquals(color, expected_color)
def test_items(self):
def test_items_default_colors(self):
self.load_scenario()
items = self._get_items()
self.assertEqual(len(items), 3)
for index, item in enumerate(items):
self.assertEqual(item.get_attribute('data-value'), str(index))
self.assertEqual(item.text, self.ITEM_PROPERTIES[index]['text'])
self.assertIn('ui-draggable', self.get_element_classes(item))
self._test_item_style(item, {})
self._test_items()
@unpack
@data(
......@@ -105,21 +104,36 @@ class TestDragAndDropRender(BaseIntegrationTest):
def test_items_custom_colors(self, item_background_color, item_text_color):
self.load_scenario(item_background_color, item_text_color)
items = self._get_items()
self.assertEqual(len(items), 3)
color_settings = {}
if item_background_color:
color_settings['background-color'] = item_background_color
if item_text_color:
color_settings['color'] = item_text_color
self._test_items(color_settings=color_settings)
def _test_items(self, color_settings={}): # pylint: disable=dangerous-default-value
items = self._get_items()
self.assertEqual(len(items), 3)
for index, item in enumerate(items):
item_number = index + 1
self.assertEqual(item.get_attribute('tabindex'), '0')
self.assertEqual(item.get_attribute('draggable'), 'true')
self.assertEqual(item.get_attribute('aria-grabbed'), 'false')
self.assertEqual(item.get_attribute('data-value'), str(index))
self.assertEqual(item.text, self.ITEM_PROPERTIES[index]['text'])
self.assertIn('ui-draggable', self.get_element_classes(item))
self._test_item_style(item, color_settings)
try:
background_image = item.find_element_by_css_selector('img')
except NoSuchElementException:
self.assertEqual(item.text, self.ITEM_PROPERTIES[index]['text'])
else:
self.assertEqual(
background_image.get_attribute('alt'),
'This describes the background image of item {}'.format(item_number)
)
def test_zones(self):
self.load_scenario()
......@@ -128,19 +142,39 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.assertEqual(len(zones), 2)
self.assertEqual(zones[0].get_attribute('data-zone'), 'Zone 1')
self.assertIn('ui-droppable', self.get_element_classes(zones[0]))
self._assert_box_percentages('#zone-1', left=31.1284, top=6.17284, width=38.1323, height=36.6255)
self.assertEqual(zones[1].get_attribute('data-zone'), 'Zone 2')
self.assertIn('ui-droppable', self.get_element_classes(zones[1]))
self._assert_box_percentages('#zone-2', left=16.7315, top=43.2099, width=66.1479, height=28.8066)
box_percentages = [
{"left": 31.1284, "top": 6.17284, "width": 38.1323, "height": 36.6255},
{"left": 16.7315, "top": 43.2099, "width": 66.1479, "height": 28.8066}
]
for index, zone in enumerate(zones):
zone_number = index + 1
self.assertEqual(zone.get_attribute('tabindex'), '0')
self.assertEqual(zone.get_attribute('dropzone'), 'move')
self.assertEqual(zone.get_attribute('aria-dropeffect'), 'move')
self.assertEqual(zone.get_attribute('data-zone'), 'Zone {}'.format(zone_number))
self.assertIn('ui-droppable', self.get_element_classes(zone))
zone_box_percentages = box_percentages[index]
self._assert_box_percentages(
'#zone-{}'.format(zone_number),
left=zone_box_percentages['left'],
top=zone_box_percentages['top'],
width=zone_box_percentages['width'],
height=zone_box_percentages['height']
)
zone_name = zone.find_element_by_css_selector('p.zone-name')
self.assertEqual(zone_name.text, 'ZONE {}'.format(zone_number))
zone_description = zone.find_element_by_css_selector('p.zone-description')
self.assertEqual(zone_description.text, 'THIS DESCRIBES ZONE {}'.format(zone_number))
# Zone description should only be visible to screen readers:
self.assertEqual(zone_description.get_attribute('class'), 'zone-description sr')
def test_feedback(self):
self.load_scenario()
feedback = self._get_feedback()
feedback_message = self._get_feedback_message()
self.assertEqual(feedback.get_attribute('aria-live'), 'polite')
self.assertEqual(feedback_message.text, "Drag the items onto the image above.")
def test_background_image(self):
......@@ -149,3 +183,4 @@ class TestDragAndDropRender(BaseIntegrationTest):
bg_image = self.browser.find_element_by_css_selector(".xblock--drag-and-drop .target-img")
image_path = '/resource/drag-and-drop-v2/public/img/triangle.png'
self.assertTrue(bg_image.get_attribute("src").endswith(image_path))
self.assertEqual(bg_image.get_attribute("alt"), 'This describes the target image')
......@@ -4,6 +4,7 @@
"question_text": "Solve this <strong>drag-and-drop</strong> problem.",
"show_question_header": false,
"target_img_expanded_url": "/expanded/url/to/drag_and_drop_v2/public/img/triangle.png",
"target_img_description": "This describes the target image",
"item_background_color": "white",
"item_text_color": "#000080",
"initial_feedback": "HTML <strong>Intro</strong> Feed",
......
......@@ -69,5 +69,6 @@
"feedback": {
"start": "HTML <strong>Intro</strong> Feed",
"finish": "Final <strong>feedback</strong>!"
}
},
"targetImgDescription": "This describes the target image"
}
......@@ -4,6 +4,7 @@
"question_text": "",
"show_question_header": true,
"target_img_expanded_url": "http://i0.kym-cdn.com/photos/images/newsfeed/000/030/404/1260585284155.png",
"target_img_description": "This describes the target image",
"item_background_color": null,
"item_text_color": null,
"initial_feedback": "Intro Feed",
......
......@@ -89,5 +89,6 @@
"start": "Intro Feed",
"finish": "Final Feed"
},
"targetImg": "http://i0.kym-cdn.com/photos/images/newsfeed/000/030/404/1260585284155.png"
"targetImg": "http://i0.kym-cdn.com/photos/images/newsfeed/000/030/404/1260585284155.png",
"targetImgDescription": "This describes the target image"
}
......@@ -4,6 +4,7 @@
"question_text": "Can you solve this drag-and-drop problem?",
"show_question_header": true,
"target_img_expanded_url": "http://placehold.it/800x600",
"target_img_description": "This describes the target image",
"item_background_color": null,
"item_text_color": null,
"initial_feedback": "This is the initial feedback.",
......
......@@ -76,5 +76,6 @@
"targetImg": "http://placehold.it/800x600",
"targetImgDescription": "This describes the target image",
"displayLabels": false
}
......@@ -36,6 +36,7 @@ class BasicTests(TestCaseMixin, unittest.TestCase):
"question_text": "",
"show_question_header": True,
"target_img_expanded_url": '/expanded/url/to/drag_and_drop_v2/public/img/triangle.png',
"target_img_description": "",
"item_background_color": None,
"item_text_color": None,
"initial_feedback": DEFAULT_START_FEEDBACK,
......
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