@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.
Files changed (36) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/es/index.css +1 -0
  3. package/dist/es/index.js +15254 -15218
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +1 -0
  6. package/dist/index.js +15270 -15233
  7. package/dist/index.js.map +1 -1
  8. package/dist/strings.js +1 -0
  9. package/package.json +1 -1
  10. package/src/__stories__/hints-renderer.stories.jsx +52 -0
  11. package/src/hints-renderer.jsx +13 -4
  12. package/src/mixins/__tests__/widget-prop-blacklist_test.js +23 -0
  13. package/src/mixins/widget-prop-blacklist.js +15 -1
  14. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +30 -10
  15. package/src/styles/widgets/passage.less +1 -0
  16. package/src/types.js +1 -1
  17. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +3 -3
  18. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +7 -1
  19. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +2 -2
  20. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +30 -10
  21. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +3 -3
  22. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +6 -6
  23. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +300 -218
  24. package/src/widgets/__tests__/radio/base-radio_test.jsx +90 -6
  25. package/src/widgets/__tests__/radio/choice-icon_test.jsx +74 -0
  26. package/src/widgets/__tests__/radio/choice_test.jsx +39 -30
  27. package/src/widgets/__tests__/radio_test.jsx +228 -22
  28. package/src/widgets/graded-group-set.jsx +8 -1
  29. package/src/widgets/graded-group.jsx +2 -1
  30. package/src/widgets/passage-ref.jsx +21 -5
  31. package/src/widgets/passage.jsx +3 -1
  32. package/src/widgets/radio/__stories__/base-radio.stories.jsx +7 -3
  33. package/src/widgets/radio/base-radio.jsx +3 -1
  34. package/src/widgets/radio/choice-icon/cross-out-line.jsx +3 -2
  35. package/src/widgets/radio/choice-icon/library-choice-icon.jsx +17 -6
  36. 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 choices", () => {
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 choices", () => {
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 selected choice", () => {
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: true,
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
- userEvent.click(screen.getAllByRole("radio")[0]);
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("selects the choice by clicking the button", () => {
113
- // Arrange / Act
114
- const onChangeSpy = jest.fn();
115
- renderChoice({onChange: onChangeSpy});
116
-
117
- const button = screen.getByRole("button", {hidden: true});
118
- userEvent.click(button);
119
-
120
- // Assert
121
- expect(onChangeSpy).toHaveBeenCalledWith({
122
- checked: true,
123
- crossedOut: false,
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("selects the choice by clicking the input", () => {
187
- // Arrange / Act
188
- const onChangeSpy = jest.fn();
189
- renderChoice({onChange: onChangeSpy});
190
-
191
- const input = screen.getByRole("radio", {
192
- name: "(Choice A) This is a possible choice",
193
- });
194
- userEvent.click(input);
195
-
196
- // Assert
197
- expect(onChangeSpy).toHaveBeenCalledWith({
198
- checked: true,
199
- crossedOut: false,
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