Merge pull request #13150 from edx/clrux/ac-552-word-cloud
AC-552 word cloud accessibility updates
This commit is contained in:
@@ -188,6 +188,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
// LMS-style CAPA button for consistency with LMS buttons
|
||||
%btn-lms-style {
|
||||
border: 1px solid $btn-lms-border;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 1px 0 0 $white;
|
||||
color: $gray-d3;
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
background-color: $btn-lms-background;
|
||||
background-image: -webkit-linear-gradient($btn-lms-background,$btn-lms-gradient);
|
||||
background-image: linear-gradient($btn-lms-background,$btn-lms-gradient);
|
||||
padding: 7px 18px;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 $btn-lms-shadow;
|
||||
background-clip: padding-box;
|
||||
font-size: 0.8125em;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
box-shadow: inset 0 1px 0 0 $btn-lms-shadow-hover;
|
||||
cursor: pointer;
|
||||
background-color: $btn-lms-background-hover;
|
||||
background-image: -webkit-linear-gradient($btn-lms-background-hover,$btn-lms-gradient-hover);
|
||||
background-image: linear-gradient($btn-lms-background-hover,$btn-lms-gradient-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
border: 1px solid $btn-lms-border;
|
||||
box-shadow: inset 0 0 8px 4px $btn-lms-shadow-active,inset 0 0 8px 4px $btn-lms-shadow-active;
|
||||
}
|
||||
}
|
||||
|
||||
// +Button Element
|
||||
// ====================
|
||||
.button {
|
||||
|
||||
@@ -248,6 +248,15 @@
|
||||
color: $color-visibility-set;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
|
||||
.save {
|
||||
// taking styles from LMS for these Save buttons to maintain consistency
|
||||
// there is no studio-specific style for these LMS-styled buttons
|
||||
@extend %btn-lms-style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +Messaging - Xblocks
|
||||
|
||||
@@ -82,6 +82,17 @@ $gray-d2: shade($gray,40%);
|
||||
$gray-d3: shade($gray,60%);
|
||||
$gray-d4: shade($gray,80%);
|
||||
|
||||
// These define button styles similar to LMS
|
||||
// The goal here is consistency (until we can overhaul all of this...)
|
||||
$btn-lms-border: #d2c9c9;
|
||||
$btn-lms-background: #f1f1f1;
|
||||
$btn-lms-gradient: #d9d1d1;
|
||||
$btn-lms-shadow: #fcfbfb;
|
||||
$btn-lms-shadow-hover: #fefefe;
|
||||
$btn-lms-background-hover: #e4e4e4;
|
||||
$btn-lms-gradient-hover: #d1c9c9;
|
||||
$btn-lms-shadow-active: #cac2c2;
|
||||
|
||||
$blue: rgb(0, 159, 230);
|
||||
$blue-l1: tint($blue,20%);
|
||||
$blue-l2: tint($blue,40%);
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
|
||||
.result_cloud_section.active {
|
||||
display: block;
|
||||
width: 635px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 1em;
|
||||
|
||||
h3 {
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
.your_words{
|
||||
font-size: 0.85em;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,106 +12,110 @@
|
||||
*/
|
||||
|
||||
(function(requirejs, require, define) {
|
||||
define('WordCloudMain', [], function() {
|
||||
/**
|
||||
* @function WordCloudMain
|
||||
*
|
||||
* This function will process all the attributes from the DOM element passed, taking all of
|
||||
* the configuration attributes. It will either then attach a callback handler for the click
|
||||
* event on the button in the case when the user needs to enter words, or it will call the
|
||||
* appropriate mehtod to generate and render a word cloud from user's enetered words along with
|
||||
* all of the other words.
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {jQuery} el DOM element where the word cloud will be processed and created.
|
||||
*/
|
||||
'use strict';
|
||||
define('WordCloudMain', [
|
||||
'gettext',
|
||||
'edx-ui-toolkit/js/utils/html-utils'
|
||||
], function(gettext, HtmlUtils) {
|
||||
function generateUniqueId(wordCloudId, counter) {
|
||||
return '_wc_' + wordCloudId + '_' + counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function WordCloudMain
|
||||
*
|
||||
* This function will process all the attributes from the DOM element passed, taking all of
|
||||
* the configuration attributes. It will either then attach a callback handler for the click
|
||||
* event on the button in the case when the user needs to enter words, or it will call the
|
||||
* appropriate mehtod to generate and render a word cloud from user's enetered words along with
|
||||
* all of the other words.
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {jQuery} el DOM element where the word cloud will be processed and created.
|
||||
*/
|
||||
var WordCloudMain = function(el) {
|
||||
var _this = this;
|
||||
|
||||
this.wordCloudEl = $(el).find('.word_cloud');
|
||||
|
||||
// Get the URL to which we will post the users words.
|
||||
// Get the URL to which we will post the users words.
|
||||
this.ajax_url = this.wordCloudEl.data('ajax-url');
|
||||
|
||||
// Dimensions of the box where the word cloud will be drawn.
|
||||
// Dimensions of the box where the word cloud will be drawn.
|
||||
this.width = 635;
|
||||
this.height = 635;
|
||||
|
||||
// Hide WordCloud container before Ajax request done
|
||||
// Hide WordCloud container before Ajax request done
|
||||
this.wordCloudEl.hide();
|
||||
|
||||
// Retriveing response from the server as an AJAX request. Attach a callback that will
|
||||
// be fired on server's response.
|
||||
// Retriveing response from the server as an AJAX request. Attach a callback that will
|
||||
// be fired on server's response.
|
||||
$.postWithPrefix(
|
||||
_this.ajax_url + '/' + 'get_state', null,
|
||||
function(response) {
|
||||
if (response.status !== 'success') {
|
||||
console.log('ERROR: ' + response.error);
|
||||
_this.ajax_url + '/get_state', null,
|
||||
function(response) {
|
||||
if (response.status !== 'success') {
|
||||
return;
|
||||
}
|
||||
|
||||
_this.configJson = response;
|
||||
}
|
||||
)
|
||||
.done(function() {
|
||||
// Show WordCloud container after Ajax request done
|
||||
_this.wordCloudEl.show();
|
||||
|
||||
if (_this.configJson && _this.configJson.submitted) {
|
||||
_this.showWordCloud(_this.configJson);
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
_this.configJson = response;
|
||||
}
|
||||
)
|
||||
.done(function() {
|
||||
// Show WordCloud container after Ajax request done
|
||||
_this.wordCloudEl.show();
|
||||
|
||||
if (_this.configJson && _this.configJson.submitted) {
|
||||
_this.showWordCloud(_this.configJson);
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
$(el).find('input.save').on('click', function() {
|
||||
$(el).find('.save').on('click', function() {
|
||||
_this.submitAnswer();
|
||||
});
|
||||
}; // End-of: var WordCloudMain = function (el) {
|
||||
}; // End-of: var WordCloudMain = function(el) {
|
||||
|
||||
/**
|
||||
* @function submitAnswer
|
||||
*
|
||||
* Callback to be executed when the user eneter his words. It will send user entries to the
|
||||
* server, and upon receiving correct response, will call the function to generate the
|
||||
* word cloud.
|
||||
*/
|
||||
/**
|
||||
* @function submitAnswer
|
||||
*
|
||||
* Callback to be executed when the user eneter his words. It will send user entries to the
|
||||
* server, and upon receiving correct response, will call the function to generate the
|
||||
* word cloud.
|
||||
*/
|
||||
WordCloudMain.prototype.submitAnswer = function() {
|
||||
var _this = this,
|
||||
data = {'student_words': []};
|
||||
|
||||
// Populate the data to be sent to the server with user's words.
|
||||
// Populate the data to be sent to the server with user's words.
|
||||
this.wordCloudEl.find('input.input-cloud').each(function(index, value) {
|
||||
data.student_words.push($(value).val());
|
||||
});
|
||||
|
||||
// Send the data to the server as an AJAX request. Attach a callback that will
|
||||
// be fired on server's response.
|
||||
// Send the data to the server as an AJAX request. Attach a callback that will
|
||||
// be fired on server's response.
|
||||
$.postWithPrefix(
|
||||
_this.ajax_url + '/' + 'submit', $.param(data),
|
||||
function(response) {
|
||||
if (response.status !== 'success') {
|
||||
console.log('ERROR: ' + response.error);
|
||||
_this.ajax_url + '/submit', $.param(data),
|
||||
function(response) {
|
||||
if (response.status !== 'success') {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
_this.showWordCloud(response);
|
||||
}
|
||||
);
|
||||
}; // End-of: WordCloudMain.prototype.submitAnswer = function() {
|
||||
|
||||
_this.showWordCloud(response);
|
||||
}
|
||||
);
|
||||
}; // End-of: WordCloudMain.prototype.submitAnswer = function () {
|
||||
|
||||
/**
|
||||
* @function showWordCloud
|
||||
*
|
||||
* @param {object} response The response from the server that contains the user's entered words
|
||||
* along with all of the top words.
|
||||
*
|
||||
* This function will set up everything for d3 and launch the draw method. Among other things,
|
||||
* iw will determine maximum word size.
|
||||
*/
|
||||
/**
|
||||
* @function showWordCloud
|
||||
*
|
||||
* @param {object} response The response from the server that contains the user's entered words
|
||||
* along with all of the top words.
|
||||
*
|
||||
* This function will set up everything for d3 and launch the draw method. Among other things,
|
||||
* iw will determine maximum word size.
|
||||
*/
|
||||
WordCloudMain.prototype.showWordCloud = function(response) {
|
||||
var words,
|
||||
_this = this,
|
||||
@@ -124,9 +128,9 @@
|
||||
minSize = 10000;
|
||||
scaleFactor = 1;
|
||||
maxFontSize = 200;
|
||||
minFontSize = 15;
|
||||
minFontSize = 16;
|
||||
|
||||
// Find the word with the maximum percentage. I.e. the most popular word.
|
||||
// Find the word with the maximum percentage. I.e. the most popular word.
|
||||
$.each(words, function(index, word) {
|
||||
if (word.size > maxSize) {
|
||||
maxSize = word.size;
|
||||
@@ -136,11 +140,11 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Find the longest word, and calculate the scale appropriately. This is
|
||||
// required so that even long words fit into the drawing area.
|
||||
//
|
||||
// This is a fix for: if the word is very long and/or big, it is discarded by
|
||||
// for unknown reason.
|
||||
// Find the longest word, and calculate the scale appropriately. This is
|
||||
// required so that even long words fit into the drawing area.
|
||||
//
|
||||
// This is a fix for: if the word is very long and/or big, it is discarded by
|
||||
// for unknown reason.
|
||||
$.each(words, function(index, word) {
|
||||
var tempScaleFactor = 1.0,
|
||||
size = ((word.size / maxSize) * maxFontSize);
|
||||
@@ -154,136 +158,192 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Update the maximum font size based on the longest word.
|
||||
// Update the maximum font size based on the longest word.
|
||||
maxFontSize *= scaleFactor;
|
||||
|
||||
// Generate the word cloud.
|
||||
// Generate the word cloud.
|
||||
d3.layout.cloud().size([this.width, this.height])
|
||||
.words(words)
|
||||
.rotate(function() {
|
||||
return Math.floor((Math.random() * 2)) * 90;
|
||||
})
|
||||
.font('Impact')
|
||||
.fontSize(function(d) {
|
||||
var size = (d.size / maxSize) * maxFontSize;
|
||||
.words(words)
|
||||
.rotate(function() {
|
||||
return Math.floor((Math.random() * 2)) * 90;
|
||||
})
|
||||
.font('Impact')
|
||||
.fontSize(function(d) {
|
||||
var size = (d.size / maxSize) * maxFontSize;
|
||||
|
||||
size = size >= minFontSize ? size : minFontSize;
|
||||
size = size >= minFontSize ? size : minFontSize;
|
||||
|
||||
return size;
|
||||
})
|
||||
.on('end', function(words, bounds) {
|
||||
// Draw the word cloud.
|
||||
_this.drawWordCloud(response, words, bounds);
|
||||
})
|
||||
.start();
|
||||
}; // End-of: WordCloudMain.prototype.showWordCloud = function (response) {
|
||||
return size;
|
||||
})
|
||||
.on('end', function(words, bounds) { // eslint-disable-line no-shadow
|
||||
// Draw the word cloud.
|
||||
_this.drawWordCloud(response, words, bounds);
|
||||
})
|
||||
.start();
|
||||
}; // End-of: WordCloudMain.prototype.showWordCloud = function(response) {
|
||||
|
||||
/**
|
||||
* @function drawWordCloud
|
||||
*
|
||||
* This function will be called when d3 has finished initing the state for our word cloud,
|
||||
* and it is ready to hand off the process to the drawing routine. Basically set up everything
|
||||
* necessary for the actual drwing of the words.
|
||||
*
|
||||
* @param {object} response The response from the server that contains the user's entered words
|
||||
* along with all of the top words.
|
||||
*
|
||||
* @param {array} words An array of objects. Each object must have two properties. One property
|
||||
* is 'text' (the actual word), and the other property is 'size' which represents the number that the
|
||||
* word was enetered by the students.
|
||||
*
|
||||
* @param {array} bounds An array of two objects. First object is the top-left coordinates of the bounding
|
||||
* box where all of the words fir, second object is the bottom-right coordinates of the bounding box. Each
|
||||
* coordinate object contains two properties: 'x', and 'y'.
|
||||
*/
|
||||
/**
|
||||
* @function drawWordCloud
|
||||
*
|
||||
* This function will be called when d3 has finished initing the state for our word cloud,
|
||||
* and it is ready to hand off the process to the drawing routine. Basically set up everything
|
||||
* necessary for the actual drwing of the words.
|
||||
*
|
||||
* @param {object} response The response from the server that contains the user's entered words
|
||||
* along with all of the top words.
|
||||
*
|
||||
* @param {array} words An array of objects. Each object must have two properties. One property
|
||||
* is 'text' (the actual word), and the other property is 'size' which represents the number that the
|
||||
* word was enetered by the students.
|
||||
*
|
||||
* @param {array} bounds An array of two objects. First object is the top-left coordinates of the bounding
|
||||
* box where all of the words fir, second object is the bottom-right coordinates of the bounding box. Each
|
||||
* coordinate object contains two properties: 'x', and 'y'.
|
||||
*/
|
||||
WordCloudMain.prototype.drawWordCloud = function(response, words, bounds) {
|
||||
// Color words in different colors.
|
||||
// Color words in different colors.
|
||||
var fill = d3.scale.category20(),
|
||||
|
||||
// Will be populated by words the user enetered.
|
||||
// Will be populated by words the user enetered.
|
||||
studentWordsKeys = [],
|
||||
|
||||
// Comma separated string of user enetered words.
|
||||
// Comma separated string of user enetered words.
|
||||
studentWordsStr,
|
||||
|
||||
// By default we do not scale.
|
||||
// By default we do not scale.
|
||||
scale = 1,
|
||||
|
||||
// Caсhing of DOM element
|
||||
// Caсhing of DOM element
|
||||
cloudSectionEl = this.wordCloudEl.find('.result_cloud_section'),
|
||||
|
||||
// Needed for caсhing of d3 group elements
|
||||
groupEl;
|
||||
// Needed for caсhing of d3 group elements
|
||||
groupEl,
|
||||
|
||||
// If bounding rectangle is given, scale based on the bounding box of all the words.
|
||||
// Iterator for word cloud count for uniqueness
|
||||
wcCount = 0;
|
||||
|
||||
// If bounding rectangle is given, scale based on the bounding box of all the words.
|
||||
if (bounds) {
|
||||
scale = 0.5 * Math.min(
|
||||
this.width / Math.abs(bounds[1].x - this.width / 2),
|
||||
this.width / Math.abs(bounds[0].x - this.width / 2),
|
||||
this.height / Math.abs(bounds[1].y - this.height / 2),
|
||||
this.height / Math.abs(bounds[0].y - this.height / 2)
|
||||
);
|
||||
this.width / Math.abs(bounds[1].x - this.width / 2),
|
||||
this.width / Math.abs(bounds[0].x - this.width / 2),
|
||||
this.height / Math.abs(bounds[1].y - this.height / 2),
|
||||
this.height / Math.abs(bounds[0].y - this.height / 2)
|
||||
);
|
||||
}
|
||||
|
||||
$.each(response.student_words, function(word, stat) {
|
||||
var percent = (response.display_student_percents) ? ' ' + (Math.round(100 * (stat / response.total_count))) + '%' : '';
|
||||
|
||||
studentWordsKeys.push('<strong>' + word + '</strong>' + percent);
|
||||
studentWordsKeys.push(HtmlUtils.interpolateHtml(
|
||||
'{listStart}{startTag}{word}{endTag}{percent}{listEnd}',
|
||||
{
|
||||
listStart: HtmlUtils.HTML('<li>'),
|
||||
startTag: HtmlUtils.HTML('<strong>'),
|
||||
word: word,
|
||||
endTag: HtmlUtils.HTML('</strong>'),
|
||||
percent: percent,
|
||||
listEnd: HtmlUtils.HTML('</li>')
|
||||
}
|
||||
).toString());
|
||||
});
|
||||
studentWordsStr = '' + studentWordsKeys.join(', ');
|
||||
|
||||
studentWordsStr = '' + studentWordsKeys.join('');
|
||||
|
||||
cloudSectionEl
|
||||
.addClass('active')
|
||||
.find('.your_words').html(studentWordsStr)
|
||||
.end()
|
||||
.find('.total_num_words').html(response.total_count);
|
||||
.addClass('active');
|
||||
|
||||
HtmlUtils.setHtml(
|
||||
cloudSectionEl.find('.your_words'),
|
||||
HtmlUtils.HTML(studentWordsStr)
|
||||
);
|
||||
|
||||
HtmlUtils.setHtml(
|
||||
cloudSectionEl.find('.your_words').end().find('.total_num_words'),
|
||||
HtmlUtils.interpolateHtml(
|
||||
gettext('{start_strong}{total}{end_strong} words submitted in total.'),
|
||||
{
|
||||
start_strong: HtmlUtils.HTML('<strong>'),
|
||||
end_strong: HtmlUtils.HTML('</strong>'),
|
||||
total: response.total_count
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
$(cloudSectionEl.attr('id') + ' .word_cloud').empty();
|
||||
|
||||
// Actual drawing of word cloud.
|
||||
// Actual drawing of word cloud.
|
||||
groupEl = d3.select('#' + cloudSectionEl.attr('id') + ' .word_cloud').append('svg')
|
||||
.attr('width', this.width)
|
||||
.attr('height', this.height)
|
||||
.append('g')
|
||||
.attr('transform', 'translate(' + (0.5 * this.width) + ',' + (0.5 * this.height) + ')')
|
||||
.selectAll('text')
|
||||
.data(words)
|
||||
.enter().append('g');
|
||||
.attr('width', this.width)
|
||||
.attr('height', this.height)
|
||||
.append('g')
|
||||
.attr('transform', 'translate(' + (0.5 * this.width) + ',' + (0.5 * this.height) + ')')
|
||||
.selectAll('text')
|
||||
.data(words)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('data-id', function() {
|
||||
wcCount = wcCount + 1;
|
||||
return wcCount;
|
||||
})
|
||||
.attr('aria-describedby', function() {
|
||||
return HtmlUtils.interpolateHtml(
|
||||
gettext('text_word_{uniqueId} title_word_{uniqueId}'),
|
||||
{
|
||||
uniqueId: generateUniqueId(cloudSectionEl.attr('id'), $(this).data('id'))
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
groupEl
|
||||
.append('title')
|
||||
.text(function(d) {
|
||||
var res = '';
|
||||
.append('title')
|
||||
.attr('id', function() {
|
||||
return HtmlUtils.interpolateHtml(
|
||||
gettext('title_word_{uniqueId}'),
|
||||
{
|
||||
uniqueId: generateUniqueId(cloudSectionEl.attr('id'), $(this).parent().data('id'))
|
||||
}
|
||||
);
|
||||
})
|
||||
.text(function(d) {
|
||||
var res = '';
|
||||
|
||||
$.each(response.top_words, function(index, value) {
|
||||
if (value.text === d.text) {
|
||||
res = value.percent + '%';
|
||||
$.each(response.top_words, function(index, value) {
|
||||
if (value.text === d.text) {
|
||||
res = value.percent + '%';
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
groupEl
|
||||
.append('text')
|
||||
.style('font-size', function(d) {
|
||||
return d.size + 'px';
|
||||
})
|
||||
.style('font-family', 'Impact')
|
||||
.style('fill', function(d, i) {
|
||||
return fill(i);
|
||||
})
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('transform', function(d) {
|
||||
return 'translate(' + [d.x, d.y] + ')rotate(' + d.rotate + ')scale(' + scale + ')';
|
||||
})
|
||||
.text(function(d) {
|
||||
return d.text;
|
||||
});
|
||||
}; // End-of: WordCloudMain.prototype.drawWordCloud = function (words, bounds) {
|
||||
|
||||
.append('text')
|
||||
.attr('id', function() {
|
||||
return HtmlUtils.interpolateHtml(
|
||||
gettext('text_word_{uniqueId}'),
|
||||
{
|
||||
uniqueId: generateUniqueId(cloudSectionEl.attr('id'), $(this).parent().data('id'))
|
||||
}
|
||||
);
|
||||
})
|
||||
.style('font-size', function(d) {
|
||||
return d.size + 'px';
|
||||
})
|
||||
.style('font-family', 'Impact')
|
||||
.style('fill', function(d, i) {
|
||||
return fill(i);
|
||||
})
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('transform', function(d) {
|
||||
return 'translate(' + [d.x, d.y] + ')rotate(' + d.rotate + ')scale(' + scale + ')';
|
||||
})
|
||||
.text(function(d) {
|
||||
return d.text;
|
||||
});
|
||||
}; // End-of: WordCloudMain.prototype.drawWordCloud = function(words, bounds) {
|
||||
return WordCloudMain;
|
||||
}); // End-of: define('WordCloudMain', [], function () {
|
||||
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
|
||||
}); // End-of: define('WordCloudMain', [], function() {
|
||||
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function(requirejs, require, define) {
|
||||
|
||||
@@ -37,20 +37,25 @@ class WordCloudFields(object):
|
||||
"""XFields for word cloud."""
|
||||
display_name = String(
|
||||
display_name=_("Display Name"),
|
||||
help=_("Display name for this module"),
|
||||
help=_("The label for this word cloud on the course page."),
|
||||
scope=Scope.settings,
|
||||
default="Word cloud"
|
||||
)
|
||||
instructions = String(
|
||||
display_name=_("Instructions"),
|
||||
help=_("Add instructions to help learners understand how to use the word cloud. Clear instructions are important, especially for learners who have accessibility requirements."), # nopep8 pylint: disable=C0301
|
||||
scope=Scope.settings,
|
||||
)
|
||||
num_inputs = Integer(
|
||||
display_name=_("Inputs"),
|
||||
help=_("Number of text boxes available for students to input words/sentences."),
|
||||
help=_("The number of text boxes available for learners to add words and sentences."),
|
||||
scope=Scope.settings,
|
||||
default=5,
|
||||
values={"min": 1}
|
||||
)
|
||||
num_top_words = Integer(
|
||||
display_name=_("Maximum Words"),
|
||||
help=_("Maximum number of words to be displayed in generated word cloud."),
|
||||
help=_("The maximum number of words displayed in the generated word cloud."),
|
||||
scope=Scope.settings,
|
||||
default=250,
|
||||
values={"min": 1}
|
||||
@@ -64,7 +69,7 @@ class WordCloudFields(object):
|
||||
|
||||
# Fields for descriptor.
|
||||
submitted = Boolean(
|
||||
help=_("Whether this student has posted words to the cloud."),
|
||||
help=_("Whether this learner has posted words to the cloud."),
|
||||
scope=Scope.user_state,
|
||||
default=False
|
||||
)
|
||||
@@ -74,7 +79,7 @@ class WordCloudFields(object):
|
||||
default=[]
|
||||
)
|
||||
all_words = Dict(
|
||||
help=_("All possible words from all students."),
|
||||
help=_("All possible words from all learners."),
|
||||
scope=Scope.user_state_summary
|
||||
)
|
||||
top_words = Dict(
|
||||
@@ -235,11 +240,14 @@ class WordCloudModule(WordCloudFields, XModule):
|
||||
def get_html(self):
|
||||
"""Template rendering."""
|
||||
context = {
|
||||
'element_id': self.location.html_id(),
|
||||
'element_class': self.location.category,
|
||||
'ajax_url': self.system.ajax_url,
|
||||
'display_name': self.display_name,
|
||||
'display_name_default': WordCloudFields.display_name.default,
|
||||
'instructions': self.instructions,
|
||||
'element_class': self.location.category,
|
||||
'element_id': self.location.html_id(),
|
||||
'num_inputs': self.num_inputs,
|
||||
'submitted': self.submitted
|
||||
'submitted': self.submitted,
|
||||
}
|
||||
self.content = self.system.render_template('word_cloud.html', context)
|
||||
return self.content
|
||||
|
||||
@@ -942,8 +942,8 @@ class SpecialExamsPageAttemptsSection(PageObject):
|
||||
Clicks the "x" to remove the Student's attempt.
|
||||
"""
|
||||
with self.handle_alert(confirm=True):
|
||||
self.q(css="a.remove-attempt").first.click()
|
||||
self.wait_for_element_absence("a.remove-attempt", "exam attempt")
|
||||
self.q(css=".remove-attempt").first.click()
|
||||
self.wait_for_element_absence(".remove-attempt", "exam attempt")
|
||||
|
||||
|
||||
class DataDownloadPage(PageObject):
|
||||
|
||||
@@ -20,7 +20,7 @@ def view_word_cloud(_step):
|
||||
|
||||
@step('I press the Save button')
|
||||
def press_the_save_button(_step):
|
||||
button_css = '.input_cloud_section input.save'
|
||||
button_css = '.input_cloud_section .save'
|
||||
world.css_click(button_css)
|
||||
|
||||
|
||||
|
||||
@@ -241,14 +241,16 @@ class TestWordCloud(BaseTestXmodule):
|
||||
)
|
||||
|
||||
def test_word_cloud_constructor(self):
|
||||
"""Make sure that all parameters extracted correclty from xml"""
|
||||
"""Make sure that all parameters extracted correctly from xml"""
|
||||
fragment = self.runtime.render(self.item_descriptor, STUDENT_VIEW)
|
||||
|
||||
expected_context = {
|
||||
'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url,
|
||||
'display_name': self.item_descriptor.display_name,
|
||||
'display_name_default': 'Word cloud',
|
||||
'instructions': self.item_descriptor.instructions,
|
||||
'element_class': self.item_descriptor.location.category,
|
||||
'element_id': self.item_descriptor.location.html_id(),
|
||||
'num_inputs': 5, # default value
|
||||
'submitted': False # default value
|
||||
'submitted': False, # default value
|
||||
}
|
||||
self.assertEqual(fragment.content, self.runtime.render_template('word_cloud.html', expected_context))
|
||||
|
||||
@@ -1,30 +1,52 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<section
|
||||
<div
|
||||
id="word_cloud_${element_id}"
|
||||
class="${element_class}"
|
||||
data-ajax-url="${ajax_url}"
|
||||
>
|
||||
|
||||
<section class="input_cloud_section">
|
||||
% if display_name:
|
||||
<h3 class="hd hd-3" id="word_cloud_${element_id}_heading">${display_name}</h3>
|
||||
% endif
|
||||
|
||||
% if instructions is not None:
|
||||
<div class="input_cloud_section" role="group" aria-labelledby="word_cloud_${element_id}_instructions">
|
||||
% elif display_name:
|
||||
<div class="input_cloud_section" role="group" aria-labelledby="word_cloud_${element_id}_heading">
|
||||
% else:
|
||||
<div class="input_cloud_section" role="group">
|
||||
% endif
|
||||
|
||||
% if instructions is not None:
|
||||
<div class="input_cloud_instructions" id="word_cloud_${element_id}_instructions">
|
||||
${instructions}
|
||||
</div>
|
||||
% endif
|
||||
|
||||
% for row in range(num_inputs):
|
||||
<input
|
||||
class="input-cloud"
|
||||
${'style="display: none;"' if submitted else ''}
|
||||
type="text"
|
||||
size="40"
|
||||
/>
|
||||
<label>
|
||||
<span class="sr">${_('{num} of {total}').format(num=row+1, total=num_inputs)}</span>
|
||||
<input
|
||||
class="input-cloud"
|
||||
${'style="display: none;"' if submitted else ''}
|
||||
type="text"
|
||||
size="40"
|
||||
/>
|
||||
</label>
|
||||
% endfor
|
||||
|
||||
<section class="action">
|
||||
<input class="save" type="button" value="${_('Save')}" />
|
||||
</section>
|
||||
</section>
|
||||
<div class="action">
|
||||
<button class="save" type="button">${_('Save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section id="result_cloud_section_${element_id}" class="result_cloud_section">
|
||||
<h3>${_('Your words:')} <span class="your_words"></span></h3>
|
||||
<h3>${_('Total number of words:')} <span class="total_num_words"></span></h3>
|
||||
<div id="result_cloud_section_${element_id}" class="result_cloud_section">
|
||||
<div class="word_cloud"></div>
|
||||
</section>
|
||||
<p class="total_num_words"></p>
|
||||
<p>${_('Your words were:')}</p>
|
||||
<ul class="your_words"></ul>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user