Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xblock-drag-and-drop-v2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
xblock-drag-and-drop-v2
Commits
6b977414
Commit
6b977414
authored
Jan 18, 2016
by
Braden MacDonald
Browse files
Options
Browse Files
Download
Plain Diff
Update this branch with latest changes from master
# Conflicts: # tests/integration/test_interaction.py
parents
9e32e448
d31dce44
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
409 additions
and
193 deletions
+409
-193
.travis.yml
+1
-0
README.md
+257
-0
drag_and_drop_v2/drag_and_drop_v2.py
+1
-7
drag_and_drop_v2/public/css/drag_and_drop.css
+1
-12
drag_and_drop_v2/public/css/drag_and_drop_edit.css
+0
-11
drag_and_drop_v2/public/js/drag_and_drop.js
+40
-27
drag_and_drop_v2/public/js/drag_and_drop_edit.js
+0
-16
drag_and_drop_v2/public/js/vendor/jquery.html5-placeholder-shim.js
+0
-109
drag_and_drop_v2/public/js/view.js
+0
-1
drag_and_drop_v2/public/themes/apros.css
+1
-1
setup.py
+2
-1
tests/integration/test_interaction.py
+106
-8
No files found.
.travis.yml
View file @
6b977414
language
:
python
language
:
python
sudo
:
false
python
:
python
:
-
"
2.7"
-
"
2.7"
before_install
:
before_install
:
...
...
README.md
View file @
6b977414
...
@@ -137,6 +137,263 @@ any zone.
...
@@ -137,6 +137,263 @@ any zone.
You can define an arbitrary number of drag items.
You can define an arbitrary number of drag items.
Analytics Events
----------------
The following analytics events are provided by this block.
## `edx.drag_and_drop_v2.loaded`
Fired when the Drag and Drop XBlock is finished loading.
Example ("common" fields that are not interesting in this context have been left out):
```
{
...
"event": {},
"event_source": "server", -- Common field, contains event source.
"event_type": "edx.drag_and_drop_v2.loaded", -- Common field, contains event name.
...
```
Real event example (taken from a devstack):
```
{
"username": "staff",
"event_type": "edx.drag_and_drop_v2.loaded",
"ip": "10.0.2.2",
"agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0",
"host": "precise64",
"referer": "http://example.com/courses/course-v1:DnD+DnD+DnD/courseware/ec546c58d2f447b7a9223c57b5de7344/756071f8de7f47c3b0ae726586ebbe16/1?activate_block_id=block-v1%3ADnD%2BDnD%2BDnD%2Btype%40vertical%2Bblock%40d2fc47476ca14c55816c4a1264a27280",
"accept_language": "en;q=1.0, en;q=0.5",
"event": {},
"event_source": "server",
"context": {
"course_user_tags": {},
"user_id": 5,
"org_id": "DnD",
"module": {
"usage_key": "block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3",
"display_name": "Drag and Drop"
},
"course_id": "course-v1:DnD+DnD+DnD",
"path": "/courses/course-v1:DnD+DnD+DnD/xblock/block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3/handler/publish_event"
},
"time": "2016-01-13T01:52:41.330049+00:00",
"page": "x_module"
}
```
## `edx.drag_and_drop_v2.item.picked_up`
Fired when a student picks up a draggable item.
Example ("common" fields that are not interesting in this context have been left out):
```
{
...
"event": {
"item_id": 0, -- ID of the draggable item.
},
"event_source": "server", -- Common field, contains event source.
"event_type": "edx.drag_and_drop_v2.picked_up", -- Common field, contains event name.
...
```
Real event example (taken from a devstack):
```
{
"username": "staff",
"event_type": "edx.drag_and_drop_v2.item.picked_up",
"ip": "10.0.2.2",
"agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0",
"host": "precise64",
"referer": "http://example.com/courses/course-v1:DnD+DnD+DnD/courseware/ec546c58d2f447b7a9223c57b5de7344/756071f8de7f47c3b0ae726586ebbe16/1?activate_block_id=block-v1%3ADnD%2BDnD%2BDnD%2Btype%40vertical%2Bblock%40d2fc47476ca14c55816c4a1264a27280",
"accept_language": "en;q=1.0, en;q=0.5",
"event": {
"item_id": 0,
},
"event_source": "server",
"context": {
"course_user_tags": {},
"user_id": 5,
"org_id": "DnD",
"module": {
"usage_key": "block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3",
"display_name": "Drag and Drop"
},
"course_id": "course-v1:DnD+DnD+DnD",
"path": "/courses/course-v1:DnD+DnD+DnD/xblock/block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3/handler/publish_event"
},
"time": "2016-01-13T01:58:44.395935+00:00",
"page": "x_module"
}
```
## `edx.drag_and_drop_v2.item.dropped`
Fired when a student drops a draggable item.
This event will be emitted when a student drops a draggable item.
Example ("common" fields that are not interesting in this context have been left out):
```
{
...
"event": {
"input": null,
"is_correct": true, -- False if there is an input in the draggable item, and the student provided the wrong answer. Otherwise true.
"is_correct_location": true, -- Whether the draggable item has been placed in the correct location.
"item_id": 0, -- ID of the draggable item.
"location": "The Top Zone", -- Name of the location the item was dragged to.
},
"event_source": "server", -- Common field, contains event source.
"event_type": "edx.drag_and_drop_v2.dropped", -- Common field, contains event name.
...
```
Real event example (taken from a devstack):
```
{
"username": "staff",
"event_type": "edx.drag_and_drop_v2.item.dropped",
"ip": "10.0.2.2",
"agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0",
"host": "precise64",
"referer": "http://example.com/courses/course-v1:DnD+DnD+DnD/courseware/ec546c58d2f447b7a9223c57b5de7344/756071f8de7f47c3b0ae726586ebbe16/1?activate_block_id=block-v1%3ADnD%2BDnD%2BDnD%2Btype%40vertical%2Bblock%40d2fc47476ca14c55816c4a1264a27280",
"accept_language": "en;q=1.0, en;q=0.5",
"event": {
"is_correct_location": true,
"is_correct": true,
"location": "The Top Zone",
"item_id": 0,
"input": null
},
"event_source": "server",
"context": {
"course_user_tags": {},
"user_id": 5,
"org_id": "DnD",
"module": {
"usage_key": "block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3",
"display_name": "Drag and Drop"
},
"course_id": "course-v1:DnD+DnD+DnD",
"path": "/courses/course-v1:DnD+DnD+DnD/xblock/block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3/handler/do_attempt"
},
"time": "2016-01-13T01:58:45.202313+00:00",
"page": "x_module"
}
```
## `edx.drag_and_drop_v2.feedback.opened`
Fired when the feedback pop-up is opened.
Example ("common" fields that are not interesting in this context have been left out):
```
{
...
"event": {
"content": "Correct! This one belongs to The Top Zone.", -- Content of the feedback popup.
"truncated": false, -- Boolean indicating whether "content" field was truncated.
},
"event_source": "server", -- Common field, contains event source.
"event_type": "edx.drag_and_drop_v2.feedback.opened", -- Common field, contains event name.
...
```
Real event example (taken from a devstack):
```
{
"username": "staff",
"event_type": "edx.drag_and_drop_v2.feedback.opened",
"ip": "10.0.2.2",
"agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0",
"host": "precise64",
"referer": "http://example.com/courses/course-v1:DnD+DnD+DnD/courseware/ec546c58d2f447b7a9223c57b5de7344/756071f8de7f47c3b0ae726586ebbe16/1?activate_block_id=block-v1%3ADnD%2BDnD%2BDnD%2Btype%40vertical%2Bblock%40d2fc47476ca14c55816c4a1264a27280",
"accept_language": "en;q=1.0, en;q=0.5",
"event": {
"content": "Correct! This one belongs to The Top Zone.",
"truncated": false,
},
"event_source": "server",
"context": {
"course_user_tags": {},
"user_id": 5,
"org_id": "DnD",
"module": {
"usage_key": "block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3",
"display_name": "Drag and Drop"
},
"course_id": "course-v1:DnD+DnD+DnD",
"path": "/courses/course-v1:DnD+DnD+DnD/xblock/block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@6b80ce1e8b78426898b47a834d72ffd3/handler/publish_event"
},
"time": "2016-01-13T01:58:45.844986+00:00",
"page": "x_module"
}
```
## `edx.drag_and_drop_v2.feedback.closed`
Fired when the feedback popup is closed.
Example ("common" fields that are not interesting in this context have been left out):
```
{
...
"event": {
"content": "No, this item does not belong here. Try again." -- Message of the feedback popup that was closed.
"manually": true, -- Whether or not the user closed the feedback window manually or if it was auto-closed.
"truncated": false, -- Boolean indicating whether "content" field was truncated.
},
"event_source": "server", -- Common field, contains event source.
"event_type": "edx.drag_and_drop_v2.feedback.closed", -- Common field, contains event name.
...
```
Real event example (taken from a devstack):
```
{
"username": "staff",
"event_type": "edx.drag_and_drop_v2.feedback.closed",
"ip": "10.0.2.2",
"agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0",
"host": "precise64",
"referer": "http://example.com/courses/course-v1:DnD+DnD+DnD/courseware/ec546c58d2f447b7a9223c57b5de7344/756071f8de7f47c3b0ae726586ebbe16/1?activate_block_id=block-v1%3ADnD%2BDnD%2BDnD%2Btype%40vertical%2Bblock%40d2fc47476ca14c55816c4a1264a27280",
"accept_language": "en;q=1.0, en;q=0.5",
"event": {
"content": "No, this item does not belong here. Try again."
"manually": true
"truncated": false,
},
"event_source": "server",
"context": {
"course_user_tags": {},
"user_id": 5,
"org_id": "DnD",
"module": {
"usage_key": "block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@13d1b859a2304c858e1810ccc23f29b2",
"display_name": "Drag and Drop"
},
"course_id": "course-v1:DnD+DnD+DnD",
"path": "/courses/course-v1:DnD+DnD+DnD/xblock/block-v1:DnD+DnD+DnD+type@drag-and-drop-v2+block@13d1b859a2304c858e1810ccc23f29b2/handler/publish_event"
},
"time": "2016-01-13T02:07:00.988534+00:00",
"page": "x_module"
}
```
Testing
Testing
-------
-------
...
...
drag_and_drop_v2/drag_and_drop_v2.py
View file @
6b977414
...
@@ -198,7 +198,6 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
...
@@ -198,7 +198,6 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
)
)
js_urls
=
(
js_urls
=
(
'public/js/vendor/jquery-ui-1.10.4.custom.min.js'
,
'public/js/vendor/jquery-ui-1.10.4.custom.min.js'
,
'public/js/vendor/jquery.html5-placeholder-shim.js'
,
'public/js/vendor/handlebars-v1.1.2.js'
,
'public/js/vendor/handlebars-v1.1.2.js'
,
'public/js/drag_and_drop_edit.js'
,
'public/js/drag_and_drop_edit.js'
,
)
)
...
@@ -287,9 +286,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
...
@@ -287,9 +286,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
# so we have to figure that we're running in Studio for now
# so we have to figure that we're running in Studio for now
pass
pass
self
.
runtime
.
publish
(
self
,
'xblock.drag-and-drop-v2.item.dropped'
,
{
self
.
runtime
.
publish
(
self
,
'edx.drag_and_drop_v2.item.dropped'
,
{
'user_id'
:
self
.
scope_ids
.
user_id
,
'component_id'
:
self
.
_get_unique_id
(),
'item_id'
:
item
[
'id'
],
'item_id'
:
item
[
'id'
],
'location'
:
attempt
.
get
(
'zone'
),
'location'
:
attempt
.
get
(
'zone'
),
'input'
:
attempt
.
get
(
'input'
),
'input'
:
attempt
.
get
(
'input'
),
...
@@ -444,9 +441,6 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
...
@@ -444,9 +441,6 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
except
KeyError
:
except
KeyError
:
return
{
'result'
:
'error'
,
'message'
:
'Missing event_type in JSON data'
}
return
{
'result'
:
'error'
,
'message'
:
'Missing event_type in JSON data'
}
data
[
'user_id'
]
=
self
.
scope_ids
.
user_id
data
[
'component_id'
]
=
self
.
_get_unique_id
()
self
.
runtime
.
publish
(
self
,
event_type
,
data
)
self
.
runtime
.
publish
(
self
,
event_type
,
data
)
return
{
'result'
:
'success'
}
return
{
'result'
:
'success'
}
...
...
drag_and_drop_v2/public/css/drag_and_drop.css
View file @
6b977414
...
@@ -244,7 +244,7 @@
...
@@ -244,7 +244,7 @@
border
:
2px
solid
#a5a5a5
;
border
:
2px
solid
#a5a5a5
;
}
}
.xblock--drag-and-drop
.zone
p
{
.xblock--drag-and-drop
.
drag-container
.target
.
zone
p
{
width
:
100%
;
width
:
100%
;
font-family
:
Arial
;
font-family
:
Arial
;
font-size
:
16px
;
font-size
:
16px
;
...
@@ -255,17 +255,6 @@
...
@@ -255,17 +255,6 @@
margin-bottom
:
auto
;
margin-bottom
:
auto
;
}
}
/*** IE9 alignment fix ***/
.lt-ie10
.xblock--drag-and-drop
.zone
{
display
:
table
;
}
.lt-ie10
.xblock--drag-and-drop
.zone
p
{
display
:
table-cell
;
vertical-align
:
middle
;
text-align
:
center
;
}
/*** FEEDBACK ***/
/*** FEEDBACK ***/
.xblock--drag-and-drop
.feedback
{
.xblock--drag-and-drop
.feedback
{
...
...
drag_and_drop_v2/public/css/drag_and_drop_edit.css
View file @
6b977414
...
@@ -50,17 +50,6 @@
...
@@ -50,17 +50,6 @@
margin-bottom
:
auto
;
margin-bottom
:
auto
;
}
}
/*** IE9 alignment fix ***/
.lt-ie10
.xblock--drag-and-drop--editor
.zone
{
display
:
table
;
}
.lt-ie10
.xblock--drag-and-drop--editor
.zone
p
{
display
:
table-cell
;
vertical-align
:
middle
;
text-align
:
center
;
}
/** Builder **/
/** Builder **/
.xblock--drag-and-drop--editor
.hidden
{
.xblock--drag-and-drop--editor
.hidden
{
display
:
none
!important
;
display
:
none
!important
;
...
...
drag_and_drop_v2/public/js/drag_and_drop.js
View file @
6b977414
...
@@ -13,13 +13,15 @@ function DragAndDropBlock(runtime, element, configuration) {
...
@@ -13,13 +13,15 @@ function DragAndDropBlock(runtime, element, configuration) {
var
bgImgNaturalWidth
=
undefined
;
// pixel width of the background image (when not scaled)
var
bgImgNaturalWidth
=
undefined
;
// pixel width of the background image (when not scaled)
var
__vdom
=
virtualDom
.
h
();
// blank virtual DOM
var
__vdom
=
virtualDom
.
h
();
// blank virtual DOM
// Event string size limit.
var
MAX_LENGTH
=
255
;
// Keyboard accessibility
// Keyboard accessibility
var
ESC
=
27
;
var
ESC
=
27
;
var
RET
=
13
;
var
RET
=
13
;
var
SPC
=
32
;
var
SPC
=
32
;
var
TAB
=
9
;
var
TAB
=
9
;
var
M
=
77
;
var
M
=
77
;
var
QUESTION_MARK
=
63
;
var
placementMode
=
false
;
var
placementMode
=
false
;
var
$selectedItem
;
var
$selectedItem
;
...
@@ -48,9 +50,6 @@ function DragAndDropBlock(runtime, element, configuration) {
...
@@ -48,9 +50,6 @@ function DragAndDropBlock(runtime, element, configuration) {
// Set up event handlers:
// Set up event handlers:
$
(
document
).
on
(
'keydown mousedown touchstart'
,
closePopup
);
$
(
document
).
on
(
'keydown mousedown touchstart'
,
closePopup
);
$
(
document
).
on
(
'keypress'
,
function
(
evt
)
{
runOnKey
(
evt
,
QUESTION_MARK
,
showKeyboardHelp
);
});
$element
.
on
(
'click'
,
'.keyboard-help-button'
,
showKeyboardHelp
);
$element
.
on
(
'click'
,
'.keyboard-help-button'
,
showKeyboardHelp
);
$element
.
on
(
'keydown'
,
'.keyboard-help-button'
,
function
(
evt
)
{
$element
.
on
(
'keydown'
,
'.keyboard-help-button'
,
function
(
evt
)
{
runOnKey
(
evt
,
RET
,
showKeyboardHelp
);
runOnKey
(
evt
,
RET
,
showKeyboardHelp
);
...
@@ -69,7 +68,7 @@ function DragAndDropBlock(runtime, element, configuration) {
...
@@ -69,7 +68,7 @@ function DragAndDropBlock(runtime, element, configuration) {
initDroppable
();
initDroppable
();
// Indicate that exercise is done loading
// Indicate that exercise is done loading
publishEvent
({
event_type
:
'
xblock.drag-and-drop-
v2.loaded'
});
publishEvent
({
event_type
:
'
edx.drag_and_drop_
v2.loaded'
});
}).
fail
(
function
()
{
}).
fail
(
function
()
{
$root
.
text
(
gettext
(
"An error occurred. Unable to load drag and drop exercise."
));
$root
.
text
(
gettext
(
"An error occurred. Unable to load drag and drop exercise."
));
});
});
...
@@ -96,6 +95,15 @@ function DragAndDropBlock(runtime, element, configuration) {
...
@@ -96,6 +95,15 @@ function DragAndDropBlock(runtime, element, configuration) {
}
}
};
};
var
truncateField
=
function
(
data
,
fieldName
){
if
(
data
[
fieldName
].
length
>
MAX_LENGTH
)
{
data
[
fieldName
]
=
data
[
fieldName
].
substring
(
0
,
MAX_LENGTH
);
data
[
'truncated'
]
=
true
;
}
else
{
data
[
'truncated'
]
=
false
;
}
};
var
focusModalButton
=
function
()
{
var
focusModalButton
=
function
()
{
$root
.
find
(
'.keyboard-help-dialog .modal-dismiss-button '
).
focus
();
$root
.
find
(
'.keyboard-help-dialog .modal-dismiss-button '
).
focus
();
};
};
...
@@ -204,28 +212,26 @@ function DragAndDropBlock(runtime, element, configuration) {
...
@@ -204,28 +212,26 @@ function DragAndDropBlock(runtime, element, configuration) {
* Update the DOM to reflect 'state'.
* Update the DOM to reflect 'state'.
*/
*/
var
applyState
=
function
()
{
var
applyState
=
function
()
{
// Is there a change to the feedback popup?
// Has the feedback popup been closed?
if
(
state
.
feedback
!==
previousFeedback
)
{
if
(
state
.
closing
)
{
if
(
state
.
feedback
)
{
var
data
=
{
if
(
previousFeedback
)
{
event_type
:
'edx.drag_and_drop_v2.feedback.closed'
,
publishEvent
({
content
:
previousFeedback
||
state
.
feedback
,
event_type
:
'xblock.drag-and-drop-v2.feedback.closed'
,
manually
:
state
.
manually_closed
,
content
:
previousFeedback
,
};
manually
:
false
,
truncateField
(
data
,
'content'
);
});
publishEvent
(
data
);
delete
state
.
feedback
;
delete
state
.
closing
;
}
}
publishEvent
({
// Has feedback been set?
event_type
:
'xblock.drag-and-drop-v2.feedback.opened'
,
if
(
state
.
feedback
)
{
content
:
state
.
feedback
,
var
data
=
{
});
event_type
:
'edx.drag_and_drop_v2.feedback.opened'
,
}
else
{
publishEvent
({
event_type
:
'xblock.drag-and-drop-v2.feedback.closed'
,
content
:
state
.
feedback
,
content
:
state
.
feedback
,
manually
:
true
,
};
});
truncateField
(
data
,
'content'
);
}
publishEvent
(
data
);
previousFeedback
=
state
.
feedback
;
}
}
updateDOM
();
updateDOM
();
...
@@ -380,7 +386,7 @@ function DragAndDropBlock(runtime, element, configuration) {
...
@@ -380,7 +386,7 @@ function DragAndDropBlock(runtime, element, configuration) {
var
$item
=
$
(
this
);
var
$item
=
$
(
this
);
grabItem
(
$item
);
grabItem
(
$item
);
publishEvent
({
publishEvent
({
event_type
:
'
xblock.drag-and-drop-v2.item.picked-
up'
,
event_type
:
'
edx.drag_and_drop_v2.item.picked_
up'
,
item_id
:
$item
.
data
(
'value'
),
item_id
:
$item
.
data
(
'value'
),
});
});
},
},
...
@@ -515,7 +521,14 @@ function DragAndDropBlock(runtime, element, configuration) {
...
@@ -515,7 +521,14 @@ function DragAndDropBlock(runtime, element, configuration) {
return
;
return
;
}
}
delete
state
.
feedback
;
state
.
closing
=
true
;
previousFeedback
=
state
.
feedback
;
if
(
target
.
is
(
close_button
))
{
state
.
manually_closed
=
true
;
}
else
{
state
.
manually_closed
=
false
;
}
applyState
();
applyState
();
};
};
...
...
drag_and_drop_v2/public/js/drag_and_drop_edit.js
View file @
6b977414
...
@@ -94,9 +94,6 @@ function DragAndDropEditBlock(runtime, element, params) {
...
@@ -94,9 +94,6 @@ function DragAndDropEditBlock(runtime, element, params) {
$fbkTab
.
addClass
(
'hidden'
);
$fbkTab
.
addClass
(
'hidden'
);
$zoneTab
.
removeClass
(
'hidden'
);
$zoneTab
.
removeClass
(
'hidden'
);
// Placeholder shim for IE9
$
.
placeholder
.
shim
();
$
(
this
).
one
(
'click'
,
function
(
e
)
{
$
(
this
).
one
(
'click'
,
function
(
e
)
{
// $zoneTab -> $itemTab
// $zoneTab -> $itemTab
e
.
preventDefault
();
e
.
preventDefault
();
...
@@ -111,9 +108,6 @@ function DragAndDropEditBlock(runtime, element, params) {
...
@@ -111,9 +108,6 @@ function DragAndDropEditBlock(runtime, element, params) {
$zoneTab
.
addClass
(
'hidden'
);
$zoneTab
.
addClass
(
'hidden'
);
$itemTab
.
removeClass
(
'hidden'
);
$itemTab
.
removeClass
(
'hidden'
);
// Placeholder shim for IE9
$
.
placeholder
.
shim
();
$
(
this
).
addClass
(
'hidden'
);
$
(
this
).
addClass
(
'hidden'
);
$
(
'.save-button'
,
element
).
parent
()
$
(
'.save-button'
,
element
).
parent
()
.
removeClass
(
'hidden'
)
.
removeClass
(
'hidden'
)
...
@@ -155,8 +149,6 @@ function DragAndDropEditBlock(runtime, element, params) {
...
@@ -155,8 +149,6 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn
.
build
.
$el
.
targetImage
.
attr
(
'alt'
,
new_description
);
_fn
.
build
.
$el
.
targetImage
.
attr
(
'alt'
,
new_description
);
_fn
.
data
.
targetImgDescription
=
new_description
;
_fn
.
data
.
targetImgDescription
=
new_description
;
// Placeholder shim for IE9
$
.
placeholder
.
shim
();
})
})
.
on
(
'click'
,
'.display-labels-form input'
,
function
(
e
)
{
.
on
(
'click'
,
'.display-labels-form input'
,
function
(
e
)
{
_fn
.
data
.
displayLabels
=
$
(
'.display-labels-form input'
,
element
).
is
(
':checked'
);
_fn
.
data
.
displayLabels
=
$
(
'.display-labels-form input'
,
element
).
is
(
':checked'
);
...
@@ -219,8 +211,6 @@ function DragAndDropEditBlock(runtime, element, params) {
...
@@ -219,8 +211,6 @@ function DragAndDropEditBlock(runtime, element, params) {
// Add zone div to target
// Add zone div to target
_fn
.
build
.
form
.
zone
.
renderZonesPreview
();
_fn
.
build
.
form
.
zone
.
renderZonesPreview
();
// Placeholder shim for IE9
$
.
placeholder
.
shim
();
},
},
remove
:
function
(
e
)
{
remove
:
function
(
e
)
{
var
$el
=
$
(
e
.
currentTarget
).
closest
(
'.zone-row'
),
var
$el
=
$
(
e
.
currentTarget
).
closest
(
'.zone-row'
),
...
@@ -243,8 +233,6 @@ function DragAndDropEditBlock(runtime, element, params) {
...
@@ -243,8 +233,6 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn
.
build
.
form
.
zone
.
formCount
--
;
_fn
.
build
.
form
.
zone
.
formCount
--
;
_fn
.
build
.
form
.
zone
.
disableDelete
();
_fn
.
build
.
form
.
zone
.
disableDelete
();
// Placeholder shim for IE9
$
.
placeholder
.
shim
();
},
},
enableDelete
:
function
()
{
enableDelete
:
function
()
{
if
(
_fn
.
build
.
form
.
zone
.
formCount
>
1
)
{
if
(
_fn
.
build
.
form
.
zone
.
formCount
>
1
)
{
...
@@ -378,8 +366,6 @@ function DragAndDropEditBlock(runtime, element, params) {
...
@@ -378,8 +366,6 @@ function DragAndDropEditBlock(runtime, element, params) {
$form
.
append
(
tpl
(
ctx
));
$form
.
append
(
tpl
(
ctx
));
_fn
.
build
.
form
.
item
.
enableDelete
();
_fn
.
build
.
form
.
item
.
enableDelete
();
// Placeholder shim for IE9
$
.
placeholder
.
shim
();
},
},
remove
:
function
(
e
)
{
remove
:
function
(
e
)
{
var
$el
=
$
(
e
.
currentTarget
).
closest
(
'.item'
);
var
$el
=
$
(
e
.
currentTarget
).
closest
(
'.item'
);
...
@@ -390,8 +376,6 @@ function DragAndDropEditBlock(runtime, element, params) {
...
@@ -390,8 +376,6 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn
.
build
.
form
.
item
.
count
--
;
_fn
.
build
.
form
.
item
.
count
--
;
_fn
.
build
.
form
.
item
.
disableDelete
();
_fn
.
build
.
form
.
item
.
disableDelete
();
// Placeholder shim for IE9
$
.
placeholder
.
shim
();
},
},
enableDelete
:
function
()
{
enableDelete
:
function
()
{
if
(
_fn
.
build
.
form
.
item
.
count
>
1
)
{
if
(
_fn
.
build
.
form
.
item
.
count
>
1
)
{
...
...
drag_and_drop_v2/public/js/vendor/jquery.html5-placeholder-shim.js
deleted
100644 → 0
View file @
9e32e448
(
function
(
$
)
{
// @todo Document this.
$
.
extend
(
$
,{
placeholder
:
{
browser_supported
:
function
()
{
return
this
.
_supported
!==
undefined
?
this
.
_supported
:
(
this
.
_supported
=
!!
(
'placeholder'
in
$
(
'<input type="text">'
)[
0
])
);
},
shim
:
function
(
opts
)
{
var
config
=
{
color
:
'#888'
,
cls
:
'placeholder'
,
selector
:
'input[placeholder], textarea[placeholder]'
};
$
.
extend
(
config
,
opts
);
return
!
this
.
browser_supported
()
&&
$
(
config
.
selector
).
_placeholder_shim
(
config
);
}
}});
$
.
extend
(
$
.
fn
,{
_placeholder_shim
:
function
(
config
)
{
function
calcPositionCss
(
target
)
{
var
op
=
$
(
target
).
offsetParent
().
offset
();
var
ot
=
$
(
target
).
offset
();
return
{
top
:
ot
.
top
-
op
.
top
,
left
:
ot
.
left
-
op
.
left
,
width
:
$
(
target
).
width
()
};
}
function
adjustToResizing
(
label
)
{
var
$target
=
label
.
data
(
'target'
);
if
(
typeof
$target
!==
"undefined"
)
{
label
.
css
(
calcPositionCss
(
$target
));
$
(
window
).
one
(
"resize"
,
function
()
{
adjustToResizing
(
label
);
});
}
}
return
this
.
each
(
function
()
{
var
$this
=
$
(
this
);
if
(
$this
.
is
(
':visible'
)
)
{
if
(
$this
.
data
(
'placeholder'
)
)
{
var
$ol
=
$this
.
data
(
'placeholder'
);
$ol
.
css
(
calcPositionCss
(
$this
));
return
true
;
}
var
possible_line_height
=
{};
if
(
!
$this
.
is
(
'textarea'
)
&&
$this
.
css
(
'height'
)
!=
'auto'
)
{
possible_line_height
=
{
lineHeight
:
$this
.
css
(
'height'
),
whiteSpace
:
'nowrap'
};
}
var
isBorderBox
=
(
$this
.
css
(
'box-sizing'
)
===
'border-box'
);
var
ol
=
$
(
'<label />'
)
.
text
(
$this
.
attr
(
'placeholder'
))
.
addClass
(
config
.
cls
)
.
css
(
$
.
extend
({
position
:
'absolute'
,
display
:
'inline'
,
'float'
:
'none'
,
overflow
:
'hidden'
,
textAlign
:
'left'
,
color
:
config
.
color
,
cursor
:
'text'
,
paddingTop
:
isBorderBox
?
'0'
:
$this
.
css
(
'padding-top'
),
paddingRight
:
$this
.
css
(
'padding-right'
),
paddingBottom
:
isBorderBox
?
'0'
:
$this
.
css
(
'padding-bottom'
),
paddingLeft
:
$this
.
css
(
'padding-left'
),
fontSize
:
$this
.
css
(
'font-size'
),
fontFamily
:
$this
.
css
(
'font-family'
),
fontStyle
:
$this
.
css
(
'font-style'
),
fontWeight
:
$this
.
css
(
'font-weight'
),
textTransform
:
$this
.
css
(
'text-transform'
),
backgroundColor
:
'transparent'
,
zIndex
:
99
},
possible_line_height
))
.
css
(
calcPositionCss
(
this
))
.
attr
(
'for'
,
this
.
id
)
.
data
(
'target'
,
$this
)
.
click
(
function
(){
if
(
!
$
(
this
).
data
(
'target'
).
is
(
':disabled'
))
{
$
(
this
).
data
(
'target'
).
focus
();
}
})
.
insertBefore
(
this
);
$this
.
data
(
'placeholder'
,
ol
)
.
keydown
(
function
(){
ol
.
hide
();
})
.
blur
(
function
()
{
ol
[
$this
.
val
().
length
?
'hide'
:
'show'
]();
}).
triggerHandler
(
'blur'
);
$
(
window
).
one
(
"resize"
,
function
()
{
adjustToResizing
(
ol
);
});
}
});
}
});
})(
jQuery
);
jQuery
(
document
).
add
(
window
).
bind
(
'ready load'
,
function
()
{
if
(
jQuery
.
placeholder
)
{
jQuery
.
placeholder
.
shim
();
}
});
drag_and_drop_v2/public/js/view.js
View file @
6b977414
...
@@ -197,7 +197,6 @@
...
@@ -197,7 +197,6 @@
h
(
'li'
,
gettext
(
'Press "Enter", "Space", "Ctrl-m", or "⌘-m" on an item to select it for dropping, then navigate to the zone you want to drop it on.'
)),
h
(
'li'
,
gettext
(
'Press "Enter", "Space", "Ctrl-m", or "⌘-m" on an item to select it for dropping, then navigate to the zone you want to drop it on.'
)),
h
(
'li'
,
gettext
(
'Press "Enter", "Space", "Ctrl-m", or "⌘-m" to drop the item on the current zone.'
)),
h
(
'li'
,
gettext
(
'Press "Enter", "Space", "Ctrl-m", or "⌘-m" to drop the item on the current zone.'
)),
h
(
'li'
,
gettext
(
'Press "Escape" if you want to cancel the drop operation (e.g. because you would like to select a different item).'
)),
h
(
'li'
,
gettext
(
'Press "Escape" if you want to cancel the drop operation (e.g. because you would like to select a different item).'
)),
h
(
'li'
,
gettext
(
'Press "?" at any time to bring up this dialog.'
)),
])
])
]),
]),
h
(
'div.modal-actions'
,
[
h
(
'div.modal-actions'
,
[
...
...
drag_and_drop_v2/public/themes/apros.css
View file @
6b977414
...
@@ -51,7 +51,7 @@
...
@@ -51,7 +51,7 @@
background-color
:
#fff
;
background-color
:
#fff
;
}
}
.themed-xblock.xblock--drag-and-drop
.zone
p
{
.themed-xblock.xblock--drag-and-drop
.
drag-container
.target
.
zone
p
{
font-family
:
Arial
;
font-family
:
Arial
;
font-size
:
16px
;
font-size
:
16px
;
font-weight
:
bold
;
font-weight
:
bold
;
...
...
setup.py
View file @
6b977414
...
@@ -29,7 +29,8 @@ setup(
...
@@ -29,7 +29,8 @@ setup(
install_requires
=
[
install_requires
=
[
'XBlock'
,
'XBlock'
,
'xblock-utils'
,
'xblock-utils'
,
'ddt'
'ddt'
,
'mock'
,
],
],
entry_points
=
{
entry_points
=
{
'xblock.v1'
:
'drag-and-drop-v2 = drag_and_drop_v2:DragAndDropBlock'
,
'xblock.v1'
:
'drag-and-drop-v2 = drag_and_drop_v2:DragAndDropBlock'
,
...
...
tests/integration/test_interaction.py
View file @
6b977414
# Imports ###########################################################
# Imports ###########################################################
from
ddt
import
ddt
,
data
from
ddt
import
ddt
,
data
,
unpack
from
mock
import
Mock
,
patch
from
selenium.common.exceptions
import
NoSuchElementException
from
selenium.common.exceptions
import
NoSuchElementException
from
selenium.webdriver
import
ActionChains
from
selenium.webdriver
import
ActionChains
from
selenium.webdriver.common.keys
import
Keys
from
selenium.webdriver.common.keys
import
Keys
from
workbench.runtime
import
WorkbenchRuntime
from
xblockutils.resources
import
ResourceLoader
from
xblockutils.resources
import
ResourceLoader
from
drag_and_drop_v2.default_data
import
(
from
drag_and_drop_v2.default_data
import
(
...
@@ -77,6 +79,14 @@ class InteractionTestBase(object):
...
@@ -77,6 +79,14 @@ class InteractionTestBase(object):
element
=
self
.
_get_item_by_value
(
item_value
)
element
=
self
.
_get_item_by_value
(
item_value
)
return
element
.
find_element_by_class_name
(
'numerical-input'
)
return
element
.
find_element_by_class_name
(
'numerical-input'
)
def
_get_dialog_components
(
self
,
dialog
):
# pylint: disable=no-self-use
dialog_modal_overlay
=
dialog
.
find_element_by_css_selector
(
'.modal-window-overlay'
)
dialog_modal
=
dialog
.
find_element_by_css_selector
(
'.modal-window'
)
return
dialog_modal_overlay
,
dialog_modal
def
_get_dialog_dismiss_button
(
self
,
dialog_modal
):
# pylint: disable=no-self-use
return
dialog_modal
.
find_element_by_css_selector
(
'.modal-dismiss-button'
)
def
_get_zone_position
(
self
,
zone_id
):
def
_get_zone_position
(
self
,
zone_id
):
return
self
.
browser
.
execute_script
(
return
self
.
browser
.
execute_script
(
'return $("div[data-zone=
\'
{zone_id}
\'
]").prevAll(".zone").length'
.
format
(
zone_id
=
zone_id
)
'return $("div[data-zone=
\'
{zone_id}
\'
]").prevAll(".zone").length'
.
format
(
zone_id
=
zone_id
)
...
@@ -256,9 +266,8 @@ class InteractionTestBase(object):
...
@@ -256,9 +266,8 @@ class InteractionTestBase(object):
def
interact_with_keyboard_help
(
self
,
scroll_down
=
250
,
use_keyboard
=
False
):
def
interact_with_keyboard_help
(
self
,
scroll_down
=
250
,
use_keyboard
=
False
):
keyboard_help_button
=
self
.
_get_keyboard_help_button
()
keyboard_help_button
=
self
.
_get_keyboard_help_button
()
keyboard_help_dialog
=
self
.
_get_keyboard_help_dialog
()
keyboard_help_dialog
=
self
.
_get_keyboard_help_dialog
()
dialog_modal_overlay
=
keyboard_help_dialog
.
find_element_by_css_selector
(
'.modal-window-overlay'
)
dialog_modal_overlay
,
dialog_modal
=
self
.
_get_dialog_components
(
keyboard_help_dialog
)
dialog_modal
=
keyboard_help_dialog
.
find_element_by_css_selector
(
'.modal-window'
)
dialog_dismiss_button
=
self
.
_get_dialog_dismiss_button
(
dialog_modal
)
dialog_dismiss_button
=
dialog_modal
.
find_element_by_css_selector
(
'.modal-dismiss-button'
)
# Scroll "Keyboard help" button into view to make sure Selenium can successfully click it
# Scroll "Keyboard help" button into view to make sure Selenium can successfully click it
self
.
scroll_down
(
pixels
=
scroll_down
)
self
.
scroll_down
(
pixels
=
scroll_down
)
...
@@ -279,21 +288,26 @@ class InteractionTestBase(object):
...
@@ -279,21 +288,26 @@ class InteractionTestBase(object):
self
.
assertFalse
(
dialog_modal_overlay
.
is_displayed
())
self
.
assertFalse
(
dialog_modal_overlay
.
is_displayed
())
self
.
assertFalse
(
dialog_modal
.
is_displayed
())
self
.
assertFalse
(
dialog_modal
.
is_displayed
())
if
use_keyboard
:
#
Try again with "?" key
if
use_keyboard
:
#
Check if "Keyboard Help" dialog can be dismissed using "ESC"
self
.
_page
.
send_keys
(
"?"
)
keyboard_help_button
.
send_keys
(
Keys
.
RETURN
)
self
.
assertTrue
(
dialog_modal_overlay
.
is_displayed
())
self
.
assertTrue
(
dialog_modal_overlay
.
is_displayed
())
self
.
assertTrue
(
dialog_modal
.
is_displayed
())
self
.
assertTrue
(
dialog_modal
.
is_displayed
())
self
.
_page
.
send_keys
(
Keys
.
ESCAPE
)
self
.
assertFalse
(
dialog_modal_overlay
.
is_displayed
())
self
.
assertFalse
(
dialog_modal
.
is_displayed
())
def
_switch_to_block
(
self
,
idx
):
def
_switch_to_block
(
self
,
idx
):
""" Only needed if ther eare multiple blocks on the page. """
""" Only needed if ther eare multiple blocks on the page. """
self
.
_page
=
self
.
browser
.
find_elements_by_css_selector
(
self
.
default_css_selector
)[
idx
]
self
.
_page
=
self
.
browser
.
find_elements_by_css_selector
(
self
.
default_css_selector
)[
idx
]
self
.
scroll_down
(
0
)
self
.
scroll_down
(
0
)
class
BasicInteractionTest
(
InteractionTestBase
):
class
DefaultDataTestMixin
(
object
):
"""
"""
Testing interactions with Drag and Drop XBlock against default data. If default data changes this will break
.
Provides a test scenario with default options
.
"""
"""
PAGE_TITLE
=
'Drag and Drop v2'
PAGE_TITLE
=
'Drag and Drop v2'
PAGE_ID
=
'drag_and_drop_v2'
PAGE_ID
=
'drag_and_drop_v2'
...
@@ -321,6 +335,11 @@ class BasicInteractionTest(InteractionTestBase):
...
@@ -321,6 +335,11 @@ class BasicInteractionTest(InteractionTestBase):
def
_get_scenario_xml
(
self
):
# pylint: disable=no-self-use
def
_get_scenario_xml
(
self
):
# pylint: disable=no-self-use
return
"<vertical_demo><drag-and-drop-v2/></vertical_demo>"
return
"<vertical_demo><drag-and-drop-v2/></vertical_demo>"
class
BasicInteractionTest
(
DefaultDataTestMixin
,
InteractionTestBase
):
"""
Testing interactions with Drag and Drop XBlock against default data. If default data changes this will break.
"""
def
test_item_positive_feedback_on_good_move
(
self
):
def
test_item_positive_feedback_on_good_move
(
self
):
self
.
parameterized_item_positive_feedback_on_good_move
(
self
.
items_map
)
self
.
parameterized_item_positive_feedback_on_good_move
(
self
.
items_map
)
...
@@ -341,6 +360,74 @@ class BasicInteractionTest(InteractionTestBase):
...
@@ -341,6 +360,74 @@ class BasicInteractionTest(InteractionTestBase):
@ddt
@ddt
class
EventsFiredTest
(
DefaultDataTestMixin
,
InteractionTestBase
,
BaseIntegrationTest
):
"""
Tests that the analytics events are fired and in the proper order.
"""
# These events must be fired in this order.
scenarios
=
(
{
'name'
:
'edx.drag_and_drop_v2.loaded'
,
'data'
:
{},
},
{
'name'
:
'edx.drag_and_drop_v2.item.picked_up'
,
'data'
:
{
'item_id'
:
0
},
},
{
'name'
:
'grade'
,
'data'
:
{
'max_value'
:
1
,
'value'
:
(
1.0
/
3
)},
},
{
'name'
:
'edx.drag_and_drop_v2.item.dropped'
,
'data'
:
{
'input'
:
None
,
'is_correct'
:
True
,
'is_correct_location'
:
True
,
'item_id'
:
0
,
'location'
:
u'The Top Zone'
,
},
},
{
'name'
:
'edx.drag_and_drop_v2.feedback.opened'
,
'data'
:
{
'content'
:
u'Correct! This one belongs to The Top Zone.'
,
'truncated'
:
False
,
},
},
{
'name'
:
'edx.drag_and_drop_v2.feedback.closed'
,
'data'
:
{
'manually'
:
False
,
'content'
:
u'Correct! This one belongs to The Top Zone.'
,
'truncated'
:
False
,
},
},
)
def
setUp
(
self
):
mock
=
Mock
()
context
=
patch
.
object
(
WorkbenchRuntime
,
'publish'
,
mock
)
context
.
start
()
self
.
addCleanup
(
context
.
stop
)
self
.
publish
=
mock
super
(
EventsFiredTest
,
self
)
.
setUp
()
def
_get_scenario_xml
(
self
):
# pylint: disable=no-self-use
return
"<vertical_demo><drag-and-drop-v2/></vertical_demo>"
@data
(
*
enumerate
(
scenarios
))
# pylint: disable=star-args
@unpack
def
test_event
(
self
,
index
,
event
):
self
.
parameterized_item_positive_feedback_on_good_move
(
self
.
items_map
)
dummy
,
name
,
published_data
=
self
.
publish
.
call_args_list
[
index
][
0
]
self
.
assertEqual
(
name
,
event
[
'name'
])
self
.
assertEqual
(
published_data
,
event
[
'data'
]
)
@ddt
class
KeyboardInteractionTest
(
BasicInteractionTest
,
BaseIntegrationTest
):
class
KeyboardInteractionTest
(
BasicInteractionTest
,
BaseIntegrationTest
):
@data
(
Keys
.
RETURN
,
Keys
.
SPACE
,
Keys
.
CONTROL
+
'm'
,
Keys
.
COMMAND
+
'm'
)
@data
(
Keys
.
RETURN
,
Keys
.
SPACE
,
Keys
.
CONTROL
+
'm'
,
Keys
.
COMMAND
+
'm'
)
def
test_item_positive_feedback_on_good_move_with_keyboard
(
self
,
action_key
):
def
test_item_positive_feedback_on_good_move_with_keyboard
(
self
,
action_key
):
...
@@ -471,3 +558,14 @@ class MultipleBlocksDataInteraction(InteractionTestBase, BaseIntegrationTest):
...
@@ -471,3 +558,14 @@ class MultipleBlocksDataInteraction(InteractionTestBase, BaseIntegrationTest):
self
.
parameterized_final_feedback_and_reset
(
self
.
item_maps
[
'block1'
],
self
.
feedback
[
'block1'
])
self
.
parameterized_final_feedback_and_reset
(
self
.
item_maps
[
'block1'
],
self
.
feedback
[
'block1'
])
self
.
_switch_to_block
(
1
)
self
.
_switch_to_block
(
1
)
self
.
parameterized_final_feedback_and_reset
(
self
.
item_maps
[
'block2'
],
self
.
feedback
[
'block2'
],
scroll_down
=
900
)
self
.
parameterized_final_feedback_and_reset
(
self
.
item_maps
[
'block2'
],
self
.
feedback
[
'block2'
],
scroll_down
=
900
)
def
test_keyboard_help
(
self
):
self
.
_switch_to_block
(
0
)
# Test mouse and keyboard interaction
self
.
interact_with_keyboard_help
()
self
.
interact_with_keyboard_help
(
use_keyboard
=
True
)
self
.
_switch_to_block
(
1
)
# Test mouse and keyboard interaction
self
.
interact_with_keyboard_help
(
scroll_down
=
900
)
self
.
interact_with_keyboard_help
(
scroll_down
=
0
,
use_keyboard
=
True
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment