CQS: accessible score percentile chart
Closes CNVS-15301 Three enhancements: - chart now has a title (and apparently a browser tooltip!) - the 0% ... 100% X-Axis ticks are now hidden to the screenreader - the chart is equipped with an audible description, something like: "10 of the students scored above, or at, the average, and 2 below." TEST PLAN ---- ---- - go to CQS page - verify you hear the chart's title, "Score percentiles chart", labeled as a "group" - verify you can enter the group + verify that once you enter the group, you can hear the audible description of the chart noted above - verify you can not reach the 0%...100% X Axis ticks using the screen-reader NVDA NOTES ---- ----- To get to the summary sentence that is now a replacement of the chart, you can keep pushing DOWNARROW until you pass by all the table cells, and just before it reads the "Question Breakdown" heading, or alternatively (faster): - press H two times until you get to "Question Breakdown" then UPARROW - press T to get to the summary table, press DOWNARROW like 12 times to pass by all the cells, then another time Something else: if the app renders before the data is loaded, e.g, cells in the first table are still at 0%, NVDA might have already cached the page. Then, when you get to the table and the app has loaded, it reads "0%" instead of the actual numbers... you can use INSERT+F5 to tell NVDA to refresh the page and look for new changes. This is obviously problematic, but solving it is outside the scope of this patch. Maybe we should prevent the app from starting until we have all the data ready? Donno See "Refresh browse mode document" in this section of their User Guide: http://community.nvda-project.org/documentation/userGuide.html?#toc40 Change-Id: Ia4912bfb5978bf9c2fc2ae26d9a2967a5392e2f3 Reviewed-on: https://gerrit.instructure.com/41329 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Trevor deHaan <tdehaan@instructure.com> Product-Review: Derek DeVries <ddevries@instructure.com>
This commit is contained in:
parent
885dc223ae
commit
bf8ce58549
|
@ -25,6 +25,17 @@ define(function(require) {
|
|||
}
|
||||
},
|
||||
|
||||
addTitle: function(svg, title) {
|
||||
svg.append('title').text(title);
|
||||
},
|
||||
|
||||
addDescription: function(svg, description) {
|
||||
svg.append('text')
|
||||
.attr('fill', 'transparent')
|
||||
.attr('font-size', '0px')
|
||||
.text(description);
|
||||
},
|
||||
|
||||
mixin: {
|
||||
componentWillMount: function() {
|
||||
if (typeof this.createChart !== 'function') {
|
||||
|
|
|
@ -112,7 +112,11 @@ define(function(require) {
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<ScorePercentileChart key="chart" scores={this.props.scores} />
|
||||
<ScorePercentileChart
|
||||
key="chart"
|
||||
scores={this.props.scores}
|
||||
scoreAverage={this.props.scoreAverage}
|
||||
pointsPossible={this.props.pointsPossible} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ define(function(require) {
|
|||
var React = require('react');
|
||||
var ChartMixin = require('../../mixins/chart');
|
||||
var d3 = require('d3');
|
||||
var I18n = require('i18n!quiz_statistics.summary');
|
||||
var max = d3.max;
|
||||
var sum = d3.sum;
|
||||
|
||||
|
@ -19,7 +20,9 @@ define(function(require) {
|
|||
mixins: [ ChartMixin.mixin ],
|
||||
|
||||
propTypes: {
|
||||
scores: React.PropTypes.object.isRequired
|
||||
scores: React.PropTypes.object.isRequired,
|
||||
scoreAverage: React.PropTypes.number.isRequired,
|
||||
pointsPossible: React.PropTypes.number.isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
|
@ -33,6 +36,8 @@ define(function(require) {
|
|||
var highest;
|
||||
var visibilityThreshold;
|
||||
var data = this.chartData(props);
|
||||
var avgScore = props.scoreAverage / props.pointsPossible * 100.0;
|
||||
var labelOptions = this.calculateStudentStatistics(avgScore, data);
|
||||
|
||||
width = WIDTH - MARGIN_L - MARGIN_R;
|
||||
height = HEIGHT - MARGIN_T - MARGIN_B;
|
||||
|
@ -52,15 +57,25 @@ define(function(require) {
|
|||
});
|
||||
|
||||
svg = d3.select(node)
|
||||
.attr('role', 'document')
|
||||
.attr('aria-role', 'document')
|
||||
.attr('width', width + MARGIN_L + MARGIN_R)
|
||||
.attr('height', height + MARGIN_T + MARGIN_B)
|
||||
.attr('viewBox', "0 0 " + (width + MARGIN_L + MARGIN_R) + " " + (height + MARGIN_T + MARGIN_B))
|
||||
.attr('preserveAspectRatio', 'xMinYMax')
|
||||
.append('g')
|
||||
.attr("transform", "translate(" + MARGIN_L + "," + MARGIN_T + ")");
|
||||
.attr('transform', "translate(" + MARGIN_L + "," + MARGIN_T + ")")
|
||||
|
||||
ChartMixin.addTitle(svg, I18n.t('chart_title', 'Score percentiles chart'));
|
||||
ChartMixin.addDescription(svg, I18n.t('audible_chart_description',
|
||||
'%{above_average} students scored above or at the average, and %{below_average} below.', {
|
||||
above_average: labelOptions.aboveAverage,
|
||||
below_average: labelOptions.belowAverage
|
||||
}));
|
||||
|
||||
svg.append('g')
|
||||
.attr('class', 'x axis')
|
||||
.attr('aria-hidden', true)
|
||||
.attr('transform', "translate(0," + height + ")")
|
||||
.call(xAxis);
|
||||
|
||||
|
@ -82,6 +97,36 @@ define(function(require) {
|
|||
return svg;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate the number of students who scored above, or at, the average
|
||||
* and those who did lower.
|
||||
*
|
||||
* @param {Number} _avgScore
|
||||
* @param {Number[]} scores
|
||||
* The flattened score percentile data-set (see #chartData()).
|
||||
*
|
||||
* @return {Object} out
|
||||
* @return {Number} out.aboveAverage
|
||||
* @return {Number} out.belowAverage
|
||||
*/
|
||||
calculateStudentStatistics: function(_avgScore, scores) {
|
||||
var avgScore = Math.round(_avgScore);
|
||||
|
||||
return {
|
||||
aboveAverage: scores.filter(function(__y, percentile) {
|
||||
return percentile >= avgScore;
|
||||
}).reduce(function(count, y) {
|
||||
return count + y;
|
||||
}, 0),
|
||||
|
||||
belowAverage: scores.filter(function(__y, percentile) {
|
||||
return percentile < avgScore;
|
||||
}).reduce(function(count, y) {
|
||||
return count + y;
|
||||
}, 0)
|
||||
};
|
||||
},
|
||||
|
||||
chartData: function(props) {
|
||||
var percentile, upperBound;
|
||||
var set = [];
|
||||
|
|
|
@ -79,5 +79,13 @@ define(function(require) {
|
|||
{ i: 100, x: 952, y: -2, h: 182 },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('#calculateStudentStatistics', function() {
|
||||
it('should work', function() {
|
||||
var output = subject.calculateStudentStatistics(3, [ 0, 1, 1, 1, 2 ]);
|
||||
expect(output.aboveAverage).toBe(3); // includes the average
|
||||
expect(output.belowAverage).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue