Skip to content

Commit 710d646

Browse files
committed
matrix entry: style cells based on correctness
fixes #972 When the correct answer is shown, the cells of matrix entry parts are styled to show their correctness.
1 parent 9a927e5 commit 710d646

5 files changed

Lines changed: 69 additions & 3 deletions

File tree

themes/default/files/resources/exam.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,17 @@ Copyright 2011-25 Newcastle University
467467

468468
& td {
469469
text-align: center;
470+
471+
&[feedback-state="correct"] {
472+
--feedback-color: var(--success-color);
473+
}
474+
&[feedback-state="incorrect"] {
475+
--feedback-color: var(--danger-color);
476+
477+
& input {
478+
border-style: dashed;
479+
}
480+
}
470481
}
471482
}
472483

themes/default/files/scripts/answer-widgets.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ Numbas.signals.on('localisation initialised', () => {
545545
this.hasColumnHeaders = Knockout.computed(function() {
546546
return Knockout.unwrap(this.columnHeaders).length > 0;
547547
}, this);
548+
this.cellFeedback = defaultObservable(params.cellFeedback, []);
548549
this.title = params.title || '';
549550
var _numRows = typeof params.rows == 'function' ? params.rows : Knockout.observable(Knockout.unwrap(params.rows) || 2);
550551
this.numRows = Knockout.computed({
@@ -606,7 +607,16 @@ Numbas.signals.on('localisation initialised', () => {
606607
var prefilled = ((Knockout.unwrap(vm.prefilledCells) || [])[row] || [])[column];
607608
var use_prefilled = prefilled != '' && prefilled !== undefined;
608609
c = use_prefilled ? prefilled : c;
609-
var cell = {cell: Knockout.observable(c), prefilled: use_prefilled, label: R('matrix input.cell label', {row:row + 1, column:column + 1})};
610+
const feedback = Knockout.pureComputed(() => {
611+
const v = (vm.cellFeedback()[row] || [])[column];
612+
return v;
613+
});
614+
var cell = {
615+
cell: Knockout.observable(c),
616+
prefilled: use_prefilled,
617+
label: R('matrix input.cell label', {row:row + 1, column:column + 1}),
618+
feedback
619+
};
610620
cell.cell.subscribe(make_result);
611621
return cell;
612622
}
@@ -769,7 +779,7 @@ Numbas.signals.on('localisation initialised', () => {
769779
<tr>
770780
<th data-bind="visible: $parent.hasRowHeaders"><span data-bind="latex: $parent.rowHeaders()[$index()+($parent.hasColumnHeaders() ? 1 : 0)] || ''"></span></th>
771781
<!-- ko foreach: $data -->
772-
<td class="cell"><input type="text" autocapitalize="off" inputmode="text" spellcheck="false" data-bind="attr: {'aria-label': label}, textInput: cell, autosize: true, disable: prefilled || $parents[1].disable, event: $parents[1].events"/></td>
782+
<td class="cell" data-bind="attr: {'feedback-state': feedback}"><input type="text" autocapitalize="off" inputmode="text" spellcheck="false" data-bind="attr: {'aria-label': label}, textInput: cell, autosize: true, disable: prefilled || $parents[1].disable, event: $parents[1].events"/></td>
773783
<!-- /ko -->
774784
</tr>
775785
</tbody>

themes/default/files/scripts/part-display.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,14 @@ Numbas.queueScript('part-display', ['display-util', 'display-base', 'util', 'jme
274274
*/
275275
this.feedbackMessages = Knockout.observableArray([]);
276276

277+
278+
/** The values of marking notes.
279+
*
280+
* @member {observable} marking_values
281+
* @memberof Numbas.display.PartDisplay
282+
*/
283+
this.marking_values = Knockout.observable({});
284+
277285
/** Are there other parts in line with this one? (Used to decide whether to show the submit button and feedback text)
278286
* True if there's more than one part in the question, or this is a step.
279287
*
@@ -783,6 +791,7 @@ Numbas.queueScript('part-display', ['display-util', 'display-base', 'util', 'jme
783791
} else {
784792
this.feedbackMessages([]);
785793
}
794+
this.marking_values(p.marking_values);
786795
},
787796
/** Called when 'show steps' button is pressed, or coming back to a part after steps shown.
788797
*

themes/default/files/scripts/parts/matrix-display.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ Numbas.queueScript('display/parts/matrix',['display-base','part-display','util',
5353
p.storeAnswer(m);
5454
}
5555
},this);
56+
this.cellFeedback = Knockout.pureComputed(function() {
57+
let feedback = this.studentAnswer().map((row) => row.map(c => ''));
58+
if(!p.settings.markPerCell || !this.showCorrectAnswer()) {
59+
return feedback;
60+
}
61+
62+
const correct_cells = this.marking_values()?.correct_cells;
63+
64+
if(!correct_cells) {
65+
return feedback;
66+
}
67+
68+
feedback = feedback.map(row => row.map(c => 'incorrect'));
69+
70+
Numbas.jme.unwrapValue(correct_cells).map(([r,c]) => {
71+
feedback[r][c] = 'correct';
72+
});
73+
return feedback;
74+
}, this);
5675
}
5776
display.MatrixEntryPartDisplay.prototype =
5877
{

themes/default/templates/xslt/parts/matrix.xslt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
{% raw %}
22
<xsl:template match="part[@type='matrix']" mode="typespecific">
33
<xsl:if test="count(steps/part)>0"><localise>part.with steps answer prompt</localise></xsl:if>
4-
<matrix-input data-bind="attr: {{id: part.full_path+'-input'}}" params="rows: studentAnswerRows, columns: studentAnswerColumns, prefilledCells: prefilledCells, value: studentAnswer, allowResize: allowResize, minColumns: minColumns, maxColumns: maxColumns, minRows: minRows, maxRows: maxRows, disable: disabled, events: inputEvents, title: input_title"></matrix-input>
4+
<matrix-input
5+
data-bind="attr: {{id: part.full_path+'-input'}}"
6+
params="
7+
rows: studentAnswerRows,
8+
columns: studentAnswerColumns,
9+
prefilledCells: prefilledCells,
10+
value: studentAnswer,
11+
allowResize: allowResize,
12+
minColumns: minColumns,
13+
maxColumns: maxColumns,
14+
minRows: minRows,
15+
maxRows: maxRows,
16+
cellFeedback: cellFeedback,
17+
disable: disabled,
18+
events: inputEvents,
19+
title: input_title
20+
">
21+
</matrix-input>
522
{% endraw %}
623
{% include 'xslt/feedback_icon.xslt' %}
724
{% raw %}

0 commit comments

Comments
 (0)