@khanacademy/perseus 1.0.0 → 1.1.1

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 (49) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/es/index.css +17 -8
  3. package/dist/es/index.js +258 -223
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +17 -8
  6. package/dist/index.js +261 -225
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/components/__stories__/input-with-examples.stories.jsx +1 -0
  10. package/src/components/input-with-examples.jsx +41 -24
  11. package/src/components/number-input.jsx +2 -2
  12. package/src/components/simple-keypad-input.jsx +18 -21
  13. package/src/components/text-input.jsx +5 -3
  14. package/src/index.js +1 -1
  15. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +356 -65
  16. package/src/multi-items/__tests__/multi-renderer_test.js +9 -9
  17. package/src/perseus-types.js +18 -7
  18. package/src/styles/perseus-renderer.less +8 -2
  19. package/src/styles/widgets/matrix.less +2 -0
  20. package/src/styles/widgets/passage.less +9 -5
  21. package/src/util/answer-types.js +2 -3
  22. package/src/util/math.js +3 -10
  23. package/src/widgets/__stories__/numeric-input.stories.jsx +94 -7
  24. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +4 -4
  25. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +15 -6
  26. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +5 -2
  27. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +4 -1
  28. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +228 -67
  29. package/src/widgets/__tests__/__snapshots__/matrix_test.jsx.snap +24 -24
  30. package/src/widgets/__tests__/__snapshots__/numeric-input_test.jsx.snap +4 -4
  31. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +2581 -679
  32. package/src/widgets/__tests__/group_test.jsx +4 -4
  33. package/src/widgets/__tests__/numeric-input_test.jsx +12 -8
  34. package/src/widgets/__tests__/passage_test.jsx +81 -0
  35. package/src/widgets/__tests__/radio/base-radio_test.jsx +55 -7
  36. package/src/widgets/__tests__/radio/choice_test.jsx +164 -63
  37. package/src/widgets/__tests__/radio_test.jsx +39 -15
  38. package/src/widgets/definition.jsx +1 -1
  39. package/src/widgets/graded-group.jsx +5 -1
  40. package/src/widgets/group.jsx +84 -102
  41. package/src/widgets/input-number.jsx +6 -1
  42. package/src/widgets/matrix.jsx +17 -1
  43. package/src/widgets/numeric-input.jsx +99 -86
  44. package/src/widgets/radio/base-radio.jsx +3 -1
  45. package/src/widgets/radio/choice-a11y-text.js +62 -0
  46. package/src/widgets/radio/choice-icon/choice-icon.jsx +0 -68
  47. package/src/widgets/radio/choice-icon/library-choice-icon.jsx +5 -10
  48. package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +3 -12
  49. package/src/widgets/radio/choice.jsx +46 -3
@@ -123,6 +123,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
123
123
  Choose 1 answer:
124
124
  </legend>
125
125
  <div
126
+ aria-hidden="true"
126
127
  class="instructions instructions_125m8j1"
127
128
  >
128
129
  Choose 1 answer:
@@ -140,12 +141,79 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
140
141
  <div
141
142
  style="display: flex; flex-direction: row; width: 100%; opacity: 1;"
142
143
  >
144
+ <div
145
+ class="perseus-sr-only"
146
+ >
147
+ <input
148
+ id="choice-0"
149
+ tabindex="-1"
150
+ type="radio"
151
+ />
152
+ <label
153
+ for="choice-0"
154
+ >
155
+ (Choice A)
156
+  
157
+ <span
158
+ class="mock-KatexProvider"
159
+ >
160
+ <div
161
+ class="perseus-renderer perseus-renderer-responsive"
162
+ >
163
+ <div
164
+ class="paragraph"
165
+ data-perseus-paragraph-index="0"
166
+ >
167
+ <div
168
+ class="paragraph"
169
+ >
170
+ A triangle with side lengths of
171
+ <span
172
+ style="white-space: nowrap;"
173
+ >
174
+ <span />
175
+ <span
176
+ class="mock-TeX"
177
+ >
178
+ 3
179
+ </span>
180
+ <span />
181
+ </span>
182
+ ,
183
+ <span
184
+ style="white-space: nowrap;"
185
+ >
186
+ <span />
187
+ <span
188
+ class="mock-TeX"
189
+ >
190
+ 4
191
+ </span>
192
+ <span />
193
+ </span>
194
+ , and
195
+ <span
196
+ style="white-space: nowrap;"
197
+ >
198
+ <span />
199
+ <span
200
+ class="mock-TeX"
201
+ >
202
+ 5
203
+ </span>
204
+ <span />
205
+ </span>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </span>
210
+ </label>
211
+ </div>
143
212
  <button
144
- aria-checked="false"
145
213
  aria-disabled="false"
146
- aria-label="Select Choice A"
214
+ aria-hidden="true"
215
+ aria-label=""
147
216
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_bc4egv description description_psmgei"
148
- role="checkbox"
149
217
  tabindex="0"
150
218
  type="button"
151
219
  >
@@ -166,16 +234,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
166
234
  data-test-id="choice-icon__library-choice-icon"
167
235
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
168
236
  >
169
- <div
170
- class="perseus-sr-only"
171
- >
172
- (Choice A)
173
- </div>
174
- <div
175
- aria-hidden="true"
176
- >
177
- A
178
- </div>
237
+ A
179
238
  </div>
180
239
  </span>
181
240
  </div>
@@ -253,12 +312,79 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
253
312
  <div
254
313
  style="display: flex; flex-direction: row; width: 100%; opacity: 1;"
255
314
  >
315
+ <div
316
+ class="perseus-sr-only"
317
+ >
318
+ <input
319
+ id="choice-1"
320
+ tabindex="-1"
321
+ type="radio"
322
+ />
323
+ <label
324
+ for="choice-1"
325
+ >
326
+ (Choice B)
327
+  
328
+ <span
329
+ class="mock-KatexProvider"
330
+ >
331
+ <div
332
+ class="perseus-renderer perseus-renderer-responsive"
333
+ >
334
+ <div
335
+ class="paragraph"
336
+ data-perseus-paragraph-index="0"
337
+ >
338
+ <div
339
+ class="paragraph"
340
+ >
341
+ A triangle with side lengths of
342
+ <span
343
+ style="white-space: nowrap;"
344
+ >
345
+ <span />
346
+ <span
347
+ class="mock-TeX"
348
+ >
349
+ 6
350
+ </span>
351
+ <span />
352
+ </span>
353
+ ,
354
+ <span
355
+ style="white-space: nowrap;"
356
+ >
357
+ <span />
358
+ <span
359
+ class="mock-TeX"
360
+ >
361
+ 7
362
+ </span>
363
+ <span />
364
+ </span>
365
+ , and
366
+ <span
367
+ style="white-space: nowrap;"
368
+ >
369
+ <span />
370
+ <span
371
+ class="mock-TeX"
372
+ >
373
+ 10
374
+ </span>
375
+ <span />
376
+ </span>
377
+ </div>
378
+ </div>
379
+ </div>
380
+ </span>
381
+ </label>
382
+ </div>
256
383
  <button
257
- aria-checked="false"
258
384
  aria-disabled="false"
259
- aria-label="Select Choice B"
385
+ aria-hidden="true"
386
+ aria-label=""
260
387
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_bc4egv description description_psmgei"
261
- role="checkbox"
262
388
  tabindex="0"
263
389
  type="button"
264
390
  >
@@ -279,16 +405,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
279
405
  data-test-id="choice-icon__library-choice-icon"
280
406
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
281
407
  >
282
- <div
283
- class="perseus-sr-only"
284
- >
285
- (Choice B)
286
- </div>
287
- <div
288
- aria-hidden="true"
289
- >
290
- B
291
- </div>
408
+ B
292
409
  </div>
293
410
  </span>
294
411
  </div>
@@ -366,12 +483,79 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
366
483
  <div
367
484
  style="display: flex; flex-direction: row; width: 100%; opacity: 1;"
368
485
  >
486
+ <div
487
+ class="perseus-sr-only"
488
+ >
489
+ <input
490
+ id="choice-2"
491
+ tabindex="-1"
492
+ type="radio"
493
+ />
494
+ <label
495
+ for="choice-2"
496
+ >
497
+ (Choice C)
498
+  
499
+ <span
500
+ class="mock-KatexProvider"
501
+ >
502
+ <div
503
+ class="perseus-renderer perseus-renderer-responsive"
504
+ >
505
+ <div
506
+ class="paragraph"
507
+ data-perseus-paragraph-index="0"
508
+ >
509
+ <div
510
+ class="paragraph"
511
+ >
512
+ A triangle with side lengths of
513
+ <span
514
+ style="white-space: nowrap;"
515
+ >
516
+ <span />
517
+ <span
518
+ class="mock-TeX"
519
+ >
520
+ 10
521
+ </span>
522
+ <span />
523
+ </span>
524
+ ,
525
+ <span
526
+ style="white-space: nowrap;"
527
+ >
528
+ <span />
529
+ <span
530
+ class="mock-TeX"
531
+ >
532
+ 12
533
+ </span>
534
+ <span />
535
+ </span>
536
+ , and
537
+ <span
538
+ style="white-space: nowrap;"
539
+ >
540
+ <span />
541
+ <span
542
+ class="mock-TeX"
543
+ >
544
+ 18
545
+ </span>
546
+ <span />
547
+ </span>
548
+ </div>
549
+ </div>
550
+ </div>
551
+ </span>
552
+ </label>
553
+ </div>
369
554
  <button
370
- aria-checked="false"
371
555
  aria-disabled="false"
372
- aria-label="Select Choice C"
556
+ aria-hidden="true"
557
+ aria-label=""
373
558
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_bc4egv description description_psmgei"
374
- role="checkbox"
375
559
  tabindex="0"
376
560
  type="button"
377
561
  >
@@ -392,16 +576,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
392
576
  data-test-id="choice-icon__library-choice-icon"
393
577
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
394
578
  >
395
- <div
396
- class="perseus-sr-only"
397
- >
398
- (Choice C)
399
- </div>
400
- <div
401
- aria-hidden="true"
402
- >
403
- C
404
- </div>
579
+ C
405
580
  </div>
406
581
  </span>
407
582
  </div>
@@ -479,12 +654,79 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
479
654
  <div
480
655
  style="display: flex; flex-direction: row; width: 100%; opacity: 1;"
481
656
  >
657
+ <div
658
+ class="perseus-sr-only"
659
+ >
660
+ <input
661
+ id="choice-3"
662
+ tabindex="-1"
663
+ type="radio"
664
+ />
665
+ <label
666
+ for="choice-3"
667
+ >
668
+ (Choice D)
669
+  
670
+ <span
671
+ class="mock-KatexProvider"
672
+ >
673
+ <div
674
+ class="perseus-renderer perseus-renderer-responsive"
675
+ >
676
+ <div
677
+ class="paragraph"
678
+ data-perseus-paragraph-index="0"
679
+ >
680
+ <div
681
+ class="paragraph"
682
+ >
683
+ A triangle with side lengths of
684
+ <span
685
+ style="white-space: nowrap;"
686
+ >
687
+ <span />
688
+ <span
689
+ class="mock-TeX"
690
+ >
691
+ 12
692
+ </span>
693
+ <span />
694
+ </span>
695
+ ,
696
+ <span
697
+ style="white-space: nowrap;"
698
+ >
699
+ <span />
700
+ <span
701
+ class="mock-TeX"
702
+ >
703
+ 14
704
+ </span>
705
+ <span />
706
+ </span>
707
+ , and
708
+ <span
709
+ style="white-space: nowrap;"
710
+ >
711
+ <span />
712
+ <span
713
+ class="mock-TeX"
714
+ >
715
+ 20
716
+ </span>
717
+ <span />
718
+ </span>
719
+ </div>
720
+ </div>
721
+ </div>
722
+ </span>
723
+ </label>
724
+ </div>
482
725
  <button
483
- aria-checked="false"
484
726
  aria-disabled="false"
485
- aria-label="Select Choice D"
727
+ aria-hidden="true"
728
+ aria-label=""
486
729
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_bc4egv description description_psmgei"
487
- role="checkbox"
488
730
  tabindex="0"
489
731
  type="button"
490
732
  >
@@ -505,16 +747,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
505
747
  data-test-id="choice-icon__library-choice-icon"
506
748
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
507
749
  >
508
- <div
509
- class="perseus-sr-only"
510
- >
511
- (Choice D)
512
- </div>
513
- <div
514
- aria-hidden="true"
515
- >
516
- D
517
- </div>
750
+ D
518
751
  </div>
519
752
  </span>
520
753
  </div>
@@ -592,12 +825,79 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
592
825
  <div
593
826
  style="display: flex; flex-direction: row; width: 100%; opacity: 1;"
594
827
  >
828
+ <div
829
+ class="perseus-sr-only"
830
+ >
831
+ <input
832
+ id="choice-4"
833
+ tabindex="-1"
834
+ type="radio"
835
+ />
836
+ <label
837
+ for="choice-4"
838
+ >
839
+ (Choice E)
840
+  
841
+ <span
842
+ class="mock-KatexProvider"
843
+ >
844
+ <div
845
+ class="perseus-renderer perseus-renderer-responsive"
846
+ >
847
+ <div
848
+ class="paragraph"
849
+ data-perseus-paragraph-index="0"
850
+ >
851
+ <div
852
+ class="paragraph"
853
+ >
854
+ A triangle with side lengths of
855
+ <span
856
+ style="white-space: nowrap;"
857
+ >
858
+ <span />
859
+ <span
860
+ class="mock-TeX"
861
+ >
862
+ 24
863
+ </span>
864
+ <span />
865
+ </span>
866
+ ,
867
+ <span
868
+ style="white-space: nowrap;"
869
+ >
870
+ <span />
871
+ <span
872
+ class="mock-TeX"
873
+ >
874
+ 28
875
+ </span>
876
+ <span />
877
+ </span>
878
+ , and
879
+ <span
880
+ style="white-space: nowrap;"
881
+ >
882
+ <span />
883
+ <span
884
+ class="mock-TeX"
885
+ >
886
+ 40
887
+ </span>
888
+ <span />
889
+ </span>
890
+ </div>
891
+ </div>
892
+ </div>
893
+ </span>
894
+ </label>
895
+ </div>
595
896
  <button
596
- aria-checked="false"
597
897
  aria-disabled="false"
598
- aria-label="Select Choice E"
898
+ aria-hidden="true"
899
+ aria-label=""
599
900
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_bc4egv description description_psmgei"
600
- role="checkbox"
601
901
  tabindex="0"
602
902
  type="button"
603
903
  >
@@ -618,16 +918,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
618
918
  data-test-id="choice-icon__library-choice-icon"
619
919
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
620
920
  >
621
- <div
622
- class="perseus-sr-only"
623
- >
624
- (Choice E)
625
- </div>
626
- <div
627
- aria-hidden="true"
628
- >
629
- E
630
- </div>
921
+ E
631
922
  </div>
632
923
  </span>
633
924
  </div>
@@ -136,7 +136,7 @@ describe("multi-item renderer", () => {
136
136
  // selected, a value is entered). You can see the result of this in the `choiceStates`
137
137
  // array in the captured state below where the choice at index 2 has
138
138
  // `"selected": true` (instead of false) and the input-number has a `currentValue`.
139
- userEvent.click(screen.getAllByRole("checkbox")[2]); // Correct
139
+ userEvent.click(screen.getAllByRole("radio")[2]); // Correct
140
140
  userEvent.paste(screen.getByRole("textbox"), "+42"); // Correct
141
141
 
142
142
  // Act
@@ -268,7 +268,7 @@ describe("multi-item renderer", () => {
268
268
  // Arrange
269
269
  const {renderer} = renderSimpleQuestion(question1);
270
270
 
271
- userEvent.click(screen.getAllByRole("checkbox")[2]);
271
+ userEvent.click(screen.getAllByRole("radio")[2]);
272
272
  userEvent.paste(screen.getByRole("textbox"), "99");
273
273
 
274
274
  // Act
@@ -525,11 +525,11 @@ describe("multi-item renderer", () => {
525
525
  renderer.restoreSerializedState(state);
526
526
 
527
527
  // Assert
528
- expect(screen.getAllByRole("checkbox")[0]).not.toBeChecked();
529
- expect(screen.getAllByRole("checkbox")[1]).not.toBeChecked();
530
- expect(screen.getAllByRole("checkbox")[2]).toBeChecked();
531
- expect(screen.getAllByRole("checkbox")[3]).not.toBeChecked();
532
- expect(screen.getAllByRole("checkbox")[4]).not.toBeChecked();
528
+ expect(screen.getAllByRole("radio")[0]).not.toBeChecked();
529
+ expect(screen.getAllByRole("radio")[1]).not.toBeChecked();
530
+ expect(screen.getAllByRole("radio")[2]).toBeChecked();
531
+ expect(screen.getAllByRole("radio")[3]).not.toBeChecked();
532
+ expect(screen.getAllByRole("radio")[4]).not.toBeChecked();
533
533
  expect(screen.getByRole("textbox")).toHaveValue("+42");
534
534
  });
535
535
 
@@ -586,7 +586,7 @@ describe("multi-item renderer", () => {
586
586
  // Arrange
587
587
  const {renderer} = renderSimpleQuestion(question1);
588
588
 
589
- userEvent.click(screen.getAllByRole("checkbox")[3]); // Correct
589
+ userEvent.click(screen.getAllByRole("radio")[3]); // Correct
590
590
  userEvent.paste(screen.getByRole("textbox"), "-42"); // Correct
591
591
 
592
592
  // Act
@@ -747,7 +747,7 @@ describe("multi-item renderer", () => {
747
747
  // Arrange
748
748
  const {renderer} = renderSimpleQuestion(question1);
749
749
 
750
- userEvent.click(screen.getAllByRole("checkbox")[3]); // Correct
750
+ userEvent.click(screen.getAllByRole("radio")[3]); // Correct
751
751
  userEvent.paste(screen.getByRole("textbox"), "-42"); // Correct
752
752
 
753
753
  // Act
@@ -706,6 +706,20 @@ export type PerseusMeasurerWidgetOptions = {|
706
706
  static: boolean,
707
707
  |};
708
708
 
709
+ export type MathFormat =
710
+ | "integer"
711
+ | "mixed"
712
+ | "improper"
713
+ | "proper"
714
+ | "decimal"
715
+ | "percent"
716
+ | "pi";
717
+
718
+ export type PerseusNumericInputAnswerForm = {|
719
+ simplify: ?("required" | "correct" | "enforced" | "optional"),
720
+ name: MathFormat,
721
+ |};
722
+
709
723
  export type PerseusNumericInputWidgetOptions = {|
710
724
  // A list of all the possible correct and incorrect answers
711
725
  answers: $ReadOnlyArray<PerseusNumericInputAnswer>,
@@ -726,22 +740,19 @@ export type PerseusNumericInputWidgetOptions = {|
726
740
 
727
741
  // Used by examples, maybe not used and should be removed in the future
728
742
  // see TODO in numeric-input
729
- answerForms?: $ReadOnlyArray<{|
730
- simplify: ?("required" | "correct" | "enforced"),
731
- name: "integer" | "decimal" | "proper" | "improper" | "mixed" | "pi",
732
- |}>,
743
+ answerForms?: $ReadOnlyArray<PerseusNumericInputAnswerForm>,
733
744
  |};
734
745
 
735
746
  export type PerseusNumericInputAnswer = {|
736
747
  // Translatable Display; A description for why this answer is correct, wrong, or ungraded
737
748
  message: string,
738
749
  // The expected answer
739
- value: ?number,
750
+ value: number,
740
751
  // Whether this answer is "correct", "wrong", or "ungraded"
741
752
  status: string,
742
753
  // The forms available for this answer. Options: "integer, ""decimal", "proper", "improper", "mixed", or "pi"
743
754
  // NOTE: perseus_data.go says this is required even though it isn't necessary.
744
- answerForms?: $ReadOnlyArray<string>,
755
+ answerForms?: $ReadOnlyArray<MathFormat>,
745
756
  // Whether the answerForms should be strictly matched
746
757
  strict: boolean,
747
758
  // A range of error +/- the value
@@ -1404,7 +1415,7 @@ export type PerseusInputNumberWidgetOptions = {|
1404
1415
  rightAlign?: boolean,
1405
1416
  simplify: "required" | "optional" | "enforced",
1406
1417
  size: "normal" | "small",
1407
- value: string | number | boolean,
1418
+ value: string | number,
1408
1419
  customKeypad?: boolean,
1409
1420
  |};
1410
1421
 
@@ -107,7 +107,10 @@
107
107
  }
108
108
 
109
109
  div.paragraph {
110
- .legacy-typography;
110
+ font-family: "Noto Serif", serif;
111
+ font-weight: 400;
112
+ font-size: 18px;
113
+ line-height: 22px;
111
114
  margin: 22px 0px;
112
115
  }
113
116
 
@@ -131,8 +134,11 @@
131
134
 
132
135
  div.instructions {
133
136
  display: block;
137
+ font-family: "Noto Serif", serif;
138
+ font-weight: 800;
139
+ font-size: 18px;
140
+ line-height: 22px;
134
141
  font-style: italic;
135
- font-weight: bold;
136
142
  }
137
143
 
138
144
  .perseus-renderer > .paragraph > ul:not(.perseus-widget-radio),
@@ -43,11 +43,13 @@
43
43
  &.bracket-left {
44
44
  border-color: @bracketColor;
45
45
  border-left-width: @borderWidth;
46
+ border-right-width: 0;
46
47
  left: 3px;
47
48
  }
48
49
 
49
50
  &.bracket-right {
50
51
  border-color: @bracketColor;
52
+ border-left-width: 0;
51
53
  border-right-width: @borderWidth;
52
54
  margin-left: (1px - 2 * @borderWidth);
53
55
  }