@khanacademy/perseus 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/es/index.css +1 -0
- package/dist/es/index.js +15254 -15218
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +1 -0
- package/dist/index.js +15270 -15233
- package/dist/index.js.map +1 -1
- package/dist/strings.js +1 -0
- package/package.json +1 -1
- package/src/__stories__/hints-renderer.stories.jsx +52 -0
- package/src/hints-renderer.jsx +13 -4
- package/src/mixins/__tests__/widget-prop-blacklist_test.js +23 -0
- package/src/mixins/widget-prop-blacklist.js +15 -1
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +30 -10
- package/src/styles/widgets/passage.less +1 -0
- package/src/types.js +1 -1
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +3 -3
- package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +7 -1
- package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +2 -2
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +30 -10
- package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +3 -3
- package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +6 -6
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +300 -218
- package/src/widgets/__tests__/radio/base-radio_test.jsx +90 -6
- package/src/widgets/__tests__/radio/choice-icon_test.jsx +74 -0
- package/src/widgets/__tests__/radio/choice_test.jsx +39 -30
- package/src/widgets/__tests__/radio_test.jsx +228 -22
- package/src/widgets/graded-group-set.jsx +8 -1
- package/src/widgets/graded-group.jsx +2 -1
- package/src/widgets/passage-ref.jsx +21 -5
- package/src/widgets/passage.jsx +3 -1
- package/src/widgets/radio/__stories__/base-radio.stories.jsx +7 -3
- package/src/widgets/radio/base-radio.jsx +3 -1
- package/src/widgets/radio/choice-icon/cross-out-line.jsx +3 -2
- package/src/widgets/radio/choice-icon/library-choice-icon.jsx +17 -6
- package/src/widgets/radio/choice.jsx +2 -1
|
@@ -54,7 +54,6 @@ describe("base-radio", () => {
|
|
|
54
54
|
it("renders a none of the above choice", () => {
|
|
55
55
|
// Arrange / Act
|
|
56
56
|
renderBaseRadio({
|
|
57
|
-
editMode: true,
|
|
58
57
|
choices: [
|
|
59
58
|
generateChoice({content: "Option 1", correct: false}),
|
|
60
59
|
generateChoice({content: "Option B", correct: false}),
|
|
@@ -82,6 +81,40 @@ describe("base-radio", () => {
|
|
|
82
81
|
expect(screen.getAllByText("None of the above")).toHaveLength(2);
|
|
83
82
|
});
|
|
84
83
|
|
|
84
|
+
it("registers as checked when none of the above choice is clicked", () => {
|
|
85
|
+
// Arrange
|
|
86
|
+
let updatedValues = null;
|
|
87
|
+
const onChangeHandler = (newValues) => {
|
|
88
|
+
updatedValues = newValues;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
renderBaseRadio({
|
|
92
|
+
choices: [
|
|
93
|
+
generateChoice({content: "Option 1", correct: false}),
|
|
94
|
+
generateChoice({content: "Option B", correct: false}),
|
|
95
|
+
generateChoice({content: "Option Gamma", correct: false}),
|
|
96
|
+
generateChoice({content: "Option Delta", correct: false}),
|
|
97
|
+
generateChoice({
|
|
98
|
+
isNoneOfTheAbove: true,
|
|
99
|
+
correct: true,
|
|
100
|
+
}),
|
|
101
|
+
],
|
|
102
|
+
onChange: onChangeHandler,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const noneOption = screen.getByRole("radio", {
|
|
106
|
+
name: "(Choice E) None of the above",
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Act
|
|
110
|
+
userEvent.click(noneOption);
|
|
111
|
+
|
|
112
|
+
// Assert
|
|
113
|
+
expect(updatedValues).toMatchObject({
|
|
114
|
+
checked: [false, false, false, false, true],
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
85
118
|
describe("edit mode", () => {
|
|
86
119
|
it("should render <li>'s for each choice", () => {
|
|
87
120
|
// Arrange / Act
|
|
@@ -130,7 +163,7 @@ describe("base-radio", () => {
|
|
|
130
163
|
});
|
|
131
164
|
|
|
132
165
|
describe("selecting and deselecting options", () => {
|
|
133
|
-
it("deselects multi-select
|
|
166
|
+
it("deselects multi-select choice", () => {
|
|
134
167
|
// Arrange
|
|
135
168
|
let updatedValues = null;
|
|
136
169
|
const onChangeHandler = (newValues) => {
|
|
@@ -177,7 +210,7 @@ describe("base-radio", () => {
|
|
|
177
210
|
});
|
|
178
211
|
|
|
179
212
|
// Equivalent to "should toggle choice when inner element clicked" but with editMode set to false
|
|
180
|
-
it("selects single select
|
|
213
|
+
it("selects single select choice", () => {
|
|
181
214
|
// Arrange
|
|
182
215
|
let updatedValues = null;
|
|
183
216
|
const onChangeHandler = (newValues) => {
|
|
@@ -190,18 +223,22 @@ describe("base-radio", () => {
|
|
|
190
223
|
generateChoice({
|
|
191
224
|
content: "Option 1",
|
|
192
225
|
correct: false,
|
|
226
|
+
checked: false,
|
|
193
227
|
}),
|
|
194
228
|
generateChoice({
|
|
195
229
|
content: "Option B",
|
|
196
230
|
correct: false,
|
|
231
|
+
checked: false,
|
|
197
232
|
}),
|
|
198
233
|
generateChoice({
|
|
199
234
|
content: "Option Gamma",
|
|
200
235
|
correct: true,
|
|
236
|
+
checked: false,
|
|
201
237
|
}),
|
|
202
238
|
generateChoice({
|
|
203
239
|
content: "Option Delta",
|
|
204
240
|
correct: false,
|
|
241
|
+
checked: false,
|
|
205
242
|
}),
|
|
206
243
|
],
|
|
207
244
|
onChange: onChangeHandler,
|
|
@@ -218,7 +255,7 @@ describe("base-radio", () => {
|
|
|
218
255
|
});
|
|
219
256
|
});
|
|
220
257
|
|
|
221
|
-
it("deselects single select
|
|
258
|
+
it("deselects single select choice", () => {
|
|
222
259
|
// Arrange
|
|
223
260
|
let updatedValues = null;
|
|
224
261
|
const onChangeHandler = (newValues) => {
|
|
@@ -231,32 +268,79 @@ describe("base-radio", () => {
|
|
|
231
268
|
generateChoice({
|
|
232
269
|
content: "Option 1",
|
|
233
270
|
correct: false,
|
|
234
|
-
checked:
|
|
271
|
+
checked: false,
|
|
235
272
|
}),
|
|
236
273
|
generateChoice({
|
|
237
274
|
content: "Option B",
|
|
238
275
|
correct: false,
|
|
276
|
+
checked: false,
|
|
239
277
|
}),
|
|
240
278
|
generateChoice({
|
|
241
279
|
content: "Option Gamma",
|
|
242
280
|
correct: true,
|
|
281
|
+
checked: true,
|
|
243
282
|
}),
|
|
244
283
|
generateChoice({
|
|
245
284
|
content: "Option Delta",
|
|
246
285
|
correct: false,
|
|
286
|
+
checked: false,
|
|
247
287
|
}),
|
|
248
288
|
],
|
|
249
289
|
onChange: onChangeHandler,
|
|
250
290
|
});
|
|
251
291
|
|
|
252
292
|
// Act
|
|
253
|
-
|
|
293
|
+
const radioButton = screen.getByRole("radio", {
|
|
294
|
+
name: "(Choice C, Checked) Option Gamma",
|
|
295
|
+
});
|
|
296
|
+
userEvent.click(radioButton);
|
|
254
297
|
|
|
255
298
|
// Assert
|
|
256
299
|
expect(updatedValues).toMatchObject({
|
|
257
300
|
checked: [false, false, false, false],
|
|
258
301
|
});
|
|
259
302
|
});
|
|
303
|
+
|
|
304
|
+
it("selects multi-select choice", () => {
|
|
305
|
+
// Arrange
|
|
306
|
+
let updatedValues = null;
|
|
307
|
+
const onChangeHandler = (newValues) => {
|
|
308
|
+
updatedValues = newValues;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
renderBaseRadio({
|
|
312
|
+
multipleSelect: true,
|
|
313
|
+
choices: [
|
|
314
|
+
generateChoice({
|
|
315
|
+
content: "Option 1",
|
|
316
|
+
correct: false,
|
|
317
|
+
}),
|
|
318
|
+
generateChoice({
|
|
319
|
+
content: "Option B",
|
|
320
|
+
correct: false,
|
|
321
|
+
}),
|
|
322
|
+
generateChoice({
|
|
323
|
+
content: "Option Gamma",
|
|
324
|
+
correct: true,
|
|
325
|
+
}),
|
|
326
|
+
generateChoice({
|
|
327
|
+
content: "Option Delta",
|
|
328
|
+
correct: false,
|
|
329
|
+
}),
|
|
330
|
+
],
|
|
331
|
+
onChange: onChangeHandler,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Act
|
|
335
|
+
userEvent.click(
|
|
336
|
+
screen.getByRole("checkbox", {name: "(Choice C) Option Gamma"}),
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
// Assert
|
|
340
|
+
expect(updatedValues).toMatchObject({
|
|
341
|
+
checked: [false, false, true, false],
|
|
342
|
+
});
|
|
343
|
+
});
|
|
260
344
|
});
|
|
261
345
|
|
|
262
346
|
describe("instruction text", () => {
|
|
@@ -64,4 +64,78 @@ describe("choice icon", () => {
|
|
|
64
64
|
},
|
|
65
65
|
);
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
it("shows a checkmark for correct answers when product is set to library", () => {
|
|
69
|
+
// Arrange & Act
|
|
70
|
+
renderChoiceIcon({
|
|
71
|
+
product: "library",
|
|
72
|
+
correct: true,
|
|
73
|
+
reviewMode: true,
|
|
74
|
+
checked: true,
|
|
75
|
+
showCorrectness: true,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const choiceWrapper = screen.getByTestId(
|
|
79
|
+
`choice-icon__library-choice-icon`,
|
|
80
|
+
);
|
|
81
|
+
const choiceAttrib = choiceWrapper.getAttribute("class");
|
|
82
|
+
|
|
83
|
+
// Assert
|
|
84
|
+
expect(choiceAttrib).toContain("circleCorrect");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("shows a dash for incorrect answers when product is set to library", () => {
|
|
88
|
+
// Arrange & Act
|
|
89
|
+
renderChoiceIcon({
|
|
90
|
+
product: "library",
|
|
91
|
+
correct: false,
|
|
92
|
+
reviewMode: true,
|
|
93
|
+
checked: true,
|
|
94
|
+
showCorrectness: true,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const choiceWrapper = screen.getByTestId(
|
|
98
|
+
`choice-icon__library-choice-icon`,
|
|
99
|
+
);
|
|
100
|
+
const choiceAttrib = choiceWrapper.getAttribute("class");
|
|
101
|
+
|
|
102
|
+
// Assert
|
|
103
|
+
expect(choiceAttrib).toContain("circleIncorrect");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("displays green border for correct answers when product is set to sat", () => {
|
|
107
|
+
// Arrange & Act
|
|
108
|
+
renderChoiceIcon({
|
|
109
|
+
product: "sat",
|
|
110
|
+
correct: true,
|
|
111
|
+
reviewMode: true,
|
|
112
|
+
checked: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const choiceWrapper = screen.getByTestId(
|
|
116
|
+
`choice-icon__sat-choice-icon`,
|
|
117
|
+
);
|
|
118
|
+
const choiceStyle = choiceWrapper.getAttribute("style");
|
|
119
|
+
|
|
120
|
+
// Assert
|
|
121
|
+
expect(choiceStyle).toContain("border-color: #009900");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("displays red border for incorrect answers when product is set to sat", () => {
|
|
125
|
+
// Arrange & Act
|
|
126
|
+
renderChoiceIcon({
|
|
127
|
+
product: "sat",
|
|
128
|
+
correct: false,
|
|
129
|
+
reviewMode: true,
|
|
130
|
+
checked: true,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const choiceWrapper = screen.getByTestId(
|
|
134
|
+
`choice-icon__sat-choice-icon`,
|
|
135
|
+
);
|
|
136
|
+
const choiceStyle = choiceWrapper.getAttribute("style");
|
|
137
|
+
|
|
138
|
+
// Assert
|
|
139
|
+
expect(choiceStyle).toContain("border-color: #990000");
|
|
140
|
+
});
|
|
67
141
|
});
|
|
@@ -109,20 +109,23 @@ describe("all choice options", () => {
|
|
|
109
109
|
|
|
110
110
|
// Tests 1 of 2 element types used to select a choice
|
|
111
111
|
describe("choice button", () => {
|
|
112
|
-
it(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
it.each([[true], [false]])(
|
|
113
|
+
"selects the choice by clicking the option when multiple select is: %s",
|
|
114
|
+
(multipleSelect: boolean) => {
|
|
115
|
+
// Arrange / Act
|
|
116
|
+
const onChangeSpy = jest.fn();
|
|
117
|
+
renderChoice({onChange: onChangeSpy, multipleSelect});
|
|
118
|
+
|
|
119
|
+
const button = screen.getByRole("button", {hidden: true});
|
|
120
|
+
userEvent.click(button);
|
|
121
|
+
|
|
122
|
+
// Assert
|
|
123
|
+
expect(onChangeSpy).toHaveBeenCalledWith({
|
|
124
|
+
checked: true,
|
|
125
|
+
crossedOut: false,
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
);
|
|
126
129
|
|
|
127
130
|
it("has correct aria-disabled when disabled", () => {
|
|
128
131
|
// Arrange / Act
|
|
@@ -183,22 +186,28 @@ describe("choice button", () => {
|
|
|
183
186
|
|
|
184
187
|
// Tests 2 of 2 element types used to select a choice
|
|
185
188
|
describe("choice input (screen reader only)", () => {
|
|
186
|
-
it(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
189
|
+
it.each([[true], [false]])(
|
|
190
|
+
"selects the choice by clicking the option when multiple select is: %s",
|
|
191
|
+
(multipleSelect: boolean) => {
|
|
192
|
+
// Arrange / Act
|
|
193
|
+
const onChangeSpy = jest.fn();
|
|
194
|
+
renderChoice({onChange: onChangeSpy, multipleSelect});
|
|
195
|
+
|
|
196
|
+
const input = screen.getByRole(
|
|
197
|
+
multipleSelect ? "checkbox" : "radio",
|
|
198
|
+
{
|
|
199
|
+
name: "(Choice A) This is a possible choice",
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
userEvent.click(input);
|
|
203
|
+
|
|
204
|
+
// Assert
|
|
205
|
+
expect(onChangeSpy).toHaveBeenCalledWith({
|
|
206
|
+
checked: true,
|
|
207
|
+
crossedOut: false,
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
);
|
|
202
211
|
|
|
203
212
|
it("registers as unchecked with checked set to false", () => {
|
|
204
213
|
// Arrange / Act
|