Skip to content

Commit d6edf18

Browse files
authored
Refine Radio's Rubric and UserInput types (#1758)
## Summary: As part of the Server Side Scoring project, this refactors Radio's Rubric type to only contain what is necessary for scoring and UserInput to only contain info about the user's input. Also adds the types to Radio's validator tests. Issue: LEMS-2471 ## Test plan: - Confirm all checks pass - Confirm Radio still works as expected via Storybook Author: Myranae Reviewers: handeyeco, catandthemachines, Myranae Required Reviewers: Approved By: catandthemachines, handeyeco Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ gerald Pull Request URL: #1758
1 parent 5cf8d97 commit d6edf18

File tree

7 files changed

+36
-32
lines changed

7 files changed

+36
-32
lines changed

.changeset/odd-dancers-relax.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@khanacademy/perseus": patch
3+
---
4+
5+
Refine Radio's Rubric and UserInput types

packages/perseus/src/multi-items/__tests__/multi-renderer.test.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,6 @@ describe("multi-item renderer", () => {
629629
true,
630630
false,
631631
],
632-
"countChoices": false,
633632
"noneOfTheAboveIndex": null,
634633
"noneOfTheAboveSelected": false,
635634
"numCorrect": 1,
@@ -785,7 +784,6 @@ describe("multi-item renderer", () => {
785784
true,
786785
false,
787786
],
788-
"countChoices": false,
789787
"noneOfTheAboveIndex": null,
790788
"noneOfTheAboveSelected": false,
791789
"numCorrect": 1,

packages/perseus/src/validation.types.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
PerseusNumericInputAnswer,
1313
PerseusOrdererWidgetOptions,
1414
PerseusPlotterWidgetOptions,
15-
PerseusRadioWidgetOptions,
15+
PerseusRadioChoice,
1616
PerseusTableWidgetOptions,
1717
} from "./perseus-types";
1818
import type {InteractiveMarkerType} from "./widgets/label-image/types";
@@ -160,10 +160,12 @@ export type PerseusPlotterRubric = PerseusPlotterWidgetOptions;
160160

161161
export type PerseusPlotterUserInput = ReadonlyArray<number>;
162162

163-
export type PerseusRadioRubric = PerseusRadioWidgetOptions;
163+
export type PerseusRadioRubric = {
164+
// The choices provided to the user.
165+
choices: ReadonlyArray<PerseusRadioChoice>;
166+
};
164167

165168
export type PerseusRadioUserInput = {
166-
countChoices?: boolean;
167169
choicesSelected: ReadonlyArray<boolean>;
168170
numCorrect?: number;
169171
noneOfTheAboveIndex?: number | null | undefined;

packages/perseus/src/widgets/group/group.test.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,6 @@ describe("group widget", () => {
429429
false,
430430
true,
431431
],
432-
"countChoices": false,
433432
"noneOfTheAboveIndex": null,
434433
"noneOfTheAboveSelected": false,
435434
"numCorrect": 1,

packages/perseus/src/widgets/radio/radio-component.tsx

-4
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ class Radio extends React.Component<Props> implements Widget {
8282

8383
const choiceStates = props.choiceStates;
8484
const choicesSelected = choiceStates.map(() => false);
85-
const countChoices = props.countChoices;
8685
const numCorrect = props.numCorrect;
8786

8887
for (let i = 0; i < choicesSelected.length; i++) {
@@ -101,7 +100,6 @@ class Radio extends React.Component<Props> implements Widget {
101100
}
102101

103102
return {
104-
countChoices,
105103
choicesSelected,
106104
numCorrect,
107105
noneOfTheAboveIndex,
@@ -116,7 +114,6 @@ class Radio extends React.Component<Props> implements Widget {
116114
let noneOfTheAboveSelected = false;
117115

118116
const choicesSelected = [...values];
119-
const countChoices = props.countChoices;
120117
const numCorrect = props.numCorrect;
121118
const valuesLength = values.length;
122119

@@ -136,7 +133,6 @@ class Radio extends React.Component<Props> implements Widget {
136133
choicesSelected,
137134
noneOfTheAboveIndex,
138135
noneOfTheAboveSelected,
139-
countChoices,
140136
numCorrect,
141137
};
142138
}

packages/perseus/src/widgets/radio/radio-validator.test.ts

+23-19
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@ import {mockStrings} from "../../strings";
22

33
import radioValidator from "./radio-validator";
44

5+
import type {
6+
PerseusRadioRubric,
7+
PerseusRadioUserInput,
8+
} from "../../validation.types";
9+
510
describe("radioValidator", () => {
611
it("is invalid when no options are selected", () => {
7-
const userInput = {
12+
const userInput: PerseusRadioUserInput = {
813
choicesSelected: [false, false, false, false],
914
};
1015

11-
const rubric = {
16+
const rubric: PerseusRadioRubric = {
1217
choices: [
1318
{content: "Choice 1"},
1419
{content: "Choice 2"},
@@ -23,12 +28,12 @@ describe("radioValidator", () => {
2328
});
2429

2530
it("is invalid when number selected does not match number correct", () => {
26-
const userInput = {
31+
const userInput: PerseusRadioUserInput = {
2732
numCorrect: 2,
2833
choicesSelected: [true, false, false, false],
2934
};
3035

31-
const rubric = {
36+
const rubric: PerseusRadioRubric = {
3237
choices: [
3338
{content: "Choice 1", correct: true},
3439
{content: "Choice 2", correct: true},
@@ -43,20 +48,19 @@ describe("radioValidator", () => {
4348
});
4449

4550
it("is invalid when none of the above and an answer are both selected", () => {
46-
const userInput = {
51+
const userInput: PerseusRadioUserInput = {
4752
noneOfTheAboveSelected: true,
4853
choicesSelected: [true, false, false, false, true],
4954
};
5055

51-
const rubric = {
56+
const rubric: PerseusRadioRubric = {
5257
choices: [
5358
{content: "Choice 1", correct: true},
5459
{content: "Choice 2", correct: false},
5560
{content: "Choice 3", correct: false},
5661
{content: "Choice 4", correct: false},
5762
{content: "None of the above", correct: false},
5863
],
59-
hasNoneOfTheAbove: true,
6064
};
6165

6266
const result = radioValidator(userInput, rubric, mockStrings);
@@ -65,11 +69,11 @@ describe("radioValidator", () => {
6569
});
6670

6771
it("can handle single correct answer", () => {
68-
const userInput = {
72+
const userInput: PerseusRadioUserInput = {
6973
choicesSelected: [true, false, false, false],
7074
};
7175

72-
const rubric = {
76+
const rubric: PerseusRadioRubric = {
7377
choices: [
7478
{content: "Choice 1", correct: true},
7579
{content: "Choice 2", correct: false},
@@ -84,11 +88,11 @@ describe("radioValidator", () => {
8488
});
8589

8690
it("can handle single incorrect answer", () => {
87-
const userInput = {
91+
const userInput: PerseusRadioUserInput = {
8892
choicesSelected: [false, false, false, true],
8993
};
9094

91-
const rubric = {
95+
const rubric: PerseusRadioRubric = {
9296
choices: [
9397
{content: "Choice 1", correct: true},
9498
{content: "Choice 2", correct: false},
@@ -103,11 +107,11 @@ describe("radioValidator", () => {
103107
});
104108

105109
it("can handle multiple correct answer", () => {
106-
const userInput = {
110+
const userInput: PerseusRadioUserInput = {
107111
choicesSelected: [true, true, false, false],
108112
};
109113

110-
const rubric = {
114+
const rubric: PerseusRadioRubric = {
111115
choices: [
112116
{content: "Choice 1", correct: true},
113117
{content: "Choice 2", correct: true},
@@ -122,11 +126,11 @@ describe("radioValidator", () => {
122126
});
123127

124128
it("can handle multiple incorrect answer", () => {
125-
const userInput = {
129+
const userInput: PerseusRadioUserInput = {
126130
choicesSelected: [true, false, false, true],
127131
};
128132

129-
const rubric = {
133+
const rubric: PerseusRadioRubric = {
130134
choices: [
131135
{content: "Choice 1", correct: true},
132136
{content: "Choice 2", correct: true},
@@ -141,13 +145,13 @@ describe("radioValidator", () => {
141145
});
142146

143147
it("can handle none of the above correct answer", () => {
144-
const userInput = {
148+
const userInput: PerseusRadioUserInput = {
145149
choicesSelected: [false, false, false, false, true],
146150
noneOfTheAboveSelected: true,
147151
noneOfTheAboveIndex: 4,
148152
};
149153

150-
const rubric = {
154+
const rubric: PerseusRadioRubric = {
151155
choices: [
152156
{content: "Choice 1", correct: false},
153157
{content: "Choice 2", correct: false},
@@ -162,13 +166,13 @@ describe("radioValidator", () => {
162166
});
163167

164168
it("can handle none of the above incorrect answer", () => {
165-
const userInput = {
169+
const userInput: PerseusRadioUserInput = {
166170
choicesSelected: [false, false, false, false, true],
167171
noneOfTheAboveSelected: true,
168172
noneOfTheAboveIndex: 4,
169173
};
170174

171-
const rubric = {
175+
const rubric: PerseusRadioRubric = {
172176
choices: [
173177
{content: "Choice 1", correct: true},
174178
{content: "Choice 2", correct: false},

packages/perseus/src/widgets/radio/radio-validator.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function radioValidator(
2121
};
2222
}
2323

24-
// TODO: should numCorrect actually be on the rubric
24+
// TODO(LEMS-2541): should numCorrect actually be on the rubric
2525
// instead of the userInput?
2626
if (
2727
userInput.numCorrect &&
@@ -35,7 +35,7 @@ function radioValidator(
3535
// If NOTA and some other answer are checked, ...
3636
}
3737

38-
// TODO: should noneOfTheAboveSelected be replaced with a
38+
// TODO(LEMS-2541): should noneOfTheAboveSelected be replaced with a
3939
// combination of choicesSelected and noneOfTheAboveIndex?
4040
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
4141
return {
@@ -46,7 +46,7 @@ function radioValidator(
4646

4747
const correct = userInput.choicesSelected.every((selected, i) => {
4848
let isCorrect: boolean;
49-
// TODO: should noneOfTheAboveIndex actually be on the rubric
49+
// TODO(LEMS-2541): should noneOfTheAboveIndex actually be on the rubric
5050
// instead of the userInput?
5151
if (userInput.noneOfTheAboveIndex === i) {
5252
isCorrect = rubric.choices.every((choice, j) => {

0 commit comments

Comments
 (0)