@khanacademy/perseus 1.2.0 → 1.3.0

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.
@@ -1,4 +1,3 @@
1
- /* eslint-disable react/sort-comp */
2
1
  // @flow
3
2
  import {linterContextDefault} from "@khanacademy/perseus-linter";
4
3
  import * as i18n from "@khanacademy/wonder-blocks-i18n";
@@ -66,6 +65,134 @@ class Radio extends React.Component<Props> {
66
65
  linterContext: linterContextDefault,
67
66
  };
68
67
 
68
+ static validate(userInput: UserInput, rubric: Rubric): PerseusScore {
69
+ const numSelected = userInput.choicesSelected.reduce(
70
+ (sum, selected) => {
71
+ return sum + (selected ? 1 : 0);
72
+ },
73
+ 0,
74
+ );
75
+
76
+ if (numSelected === 0) {
77
+ return {
78
+ type: "invalid",
79
+ message: null,
80
+ };
81
+ }
82
+
83
+ if (
84
+ userInput.numCorrect &&
85
+ userInput.numCorrect > 1 &&
86
+ numSelected !== userInput.numCorrect
87
+ ) {
88
+ return {
89
+ type: "invalid",
90
+ message: i18n._("Please choose the correct number of answers."),
91
+ };
92
+ // If NOTA and some other answer are checked, ...
93
+ }
94
+ if (userInput.noneOfTheAboveSelected && numSelected > 1) {
95
+ return {
96
+ type: "invalid",
97
+ message: i18n._(
98
+ "'None of the above' may not be selected " +
99
+ "when other answers are selected.",
100
+ ),
101
+ };
102
+ }
103
+
104
+ const correct = userInput.choicesSelected.every((selected, i) => {
105
+ let isCorrect;
106
+ if (userInput.noneOfTheAboveIndex === i) {
107
+ isCorrect = rubric.choices.every((choice, j) => {
108
+ return i === j || !choice.correct;
109
+ });
110
+ } else {
111
+ isCorrect = !!rubric.choices[i].correct;
112
+ }
113
+ return isCorrect === selected;
114
+ });
115
+
116
+ return {
117
+ type: "points",
118
+ earned: correct ? 1 : 0,
119
+ total: 1,
120
+ message: null,
121
+ };
122
+ }
123
+
124
+ static getUserInputFromProps(props: Props): UserInput {
125
+ // Return checked inputs in the form {choicesSelected: [bool]}. (Dear
126
+ // future timeline implementers: this used to be {value: i} before
127
+ // multiple select was added)
128
+ if (props.choiceStates) {
129
+ let noneOfTheAboveIndex = null;
130
+ let noneOfTheAboveSelected = false;
131
+
132
+ const choiceStates = props.choiceStates;
133
+ const choicesSelected = choiceStates.map(() => false);
134
+ const countChoices = props.countChoices;
135
+ const numCorrect = props.numCorrect;
136
+
137
+ for (let i = 0; i < choicesSelected.length; i++) {
138
+ const index = props.choices[i].originalIndex;
139
+
140
+ choicesSelected[index] = choiceStates[i].selected;
141
+
142
+ if (props.choices[i].isNoneOfTheAbove) {
143
+ noneOfTheAboveIndex = index;
144
+
145
+ if (choicesSelected[i]) {
146
+ noneOfTheAboveSelected = true;
147
+ }
148
+ }
149
+ }
150
+
151
+ return {
152
+ countChoices,
153
+ choicesSelected,
154
+ numCorrect,
155
+ noneOfTheAboveIndex,
156
+ noneOfTheAboveSelected,
157
+ };
158
+ // Support legacy choiceState implementation
159
+ }
160
+ /* istanbul ignore if - props.values is deprecated */
161
+ const {values} = props;
162
+ if (values) {
163
+ let noneOfTheAboveIndex = null;
164
+ let noneOfTheAboveSelected = false;
165
+
166
+ const choicesSelected = [...values];
167
+ const countChoices = props.countChoices;
168
+ const numCorrect = props.numCorrect;
169
+ const valuesLength = values.length;
170
+
171
+ for (let i = 0; i < valuesLength; i++) {
172
+ const index = props.choices[i].originalIndex;
173
+ choicesSelected[index] = values[i];
174
+
175
+ if (props.choices[i].isNoneOfTheAbove) {
176
+ noneOfTheAboveIndex = index;
177
+ if (choicesSelected[i]) {
178
+ noneOfTheAboveSelected = true;
179
+ }
180
+ }
181
+ }
182
+ return {
183
+ choicesSelected,
184
+ noneOfTheAboveIndex,
185
+ noneOfTheAboveSelected,
186
+ countChoices,
187
+ numCorrect,
188
+ };
189
+ }
190
+ // Nothing checked
191
+ return {
192
+ choicesSelected: props.choices.map(() => false),
193
+ };
194
+ }
195
+
69
196
  _renderRenderer: (content?: string) => React.Node = (
70
197
  content?: string = "",
71
198
  ) => {
@@ -382,134 +509,6 @@ class Radio extends React.Component<Props> {
382
509
  />
383
510
  );
384
511
  }
385
-
386
- static validate(userInput: UserInput, rubric: Rubric): PerseusScore {
387
- const numSelected = userInput.choicesSelected.reduce(
388
- (sum, selected) => {
389
- return sum + (selected ? 1 : 0);
390
- },
391
- 0,
392
- );
393
-
394
- if (numSelected === 0) {
395
- return {
396
- type: "invalid",
397
- message: null,
398
- };
399
- }
400
-
401
- if (
402
- userInput.numCorrect &&
403
- userInput.numCorrect > 1 &&
404
- numSelected !== userInput.numCorrect
405
- ) {
406
- return {
407
- type: "invalid",
408
- message: i18n._("Please choose the correct number of answers."),
409
- };
410
- // If NOTA and some other answer are checked, ...
411
- }
412
- if (userInput.noneOfTheAboveSelected && numSelected > 1) {
413
- return {
414
- type: "invalid",
415
- message: i18n._(
416
- "'None of the above' may not be selected " +
417
- "when other answers are selected.",
418
- ),
419
- };
420
- }
421
-
422
- const correct = userInput.choicesSelected.every((selected, i) => {
423
- let isCorrect;
424
- if (userInput.noneOfTheAboveIndex === i) {
425
- isCorrect = rubric.choices.every((choice, j) => {
426
- return i === j || !choice.correct;
427
- });
428
- } else {
429
- isCorrect = !!rubric.choices[i].correct;
430
- }
431
- return isCorrect === selected;
432
- });
433
-
434
- return {
435
- type: "points",
436
- earned: correct ? 1 : 0,
437
- total: 1,
438
- message: null,
439
- };
440
- }
441
-
442
- static getUserInputFromProps(props: Props): UserInput {
443
- // Return checked inputs in the form {choicesSelected: [bool]}. (Dear
444
- // future timeline implementers: this used to be {value: i} before
445
- // multiple select was added)
446
- if (props.choiceStates) {
447
- let noneOfTheAboveIndex = null;
448
- let noneOfTheAboveSelected = false;
449
-
450
- const choiceStates = props.choiceStates;
451
- const choicesSelected = choiceStates.map(() => false);
452
- const countChoices = props.countChoices;
453
- const numCorrect = props.numCorrect;
454
-
455
- for (let i = 0; i < choicesSelected.length; i++) {
456
- const index = props.choices[i].originalIndex;
457
-
458
- choicesSelected[index] = choiceStates[i].selected;
459
-
460
- if (props.choices[i].isNoneOfTheAbove) {
461
- noneOfTheAboveIndex = index;
462
-
463
- if (choicesSelected[i]) {
464
- noneOfTheAboveSelected = true;
465
- }
466
- }
467
- }
468
-
469
- return {
470
- countChoices,
471
- choicesSelected,
472
- numCorrect,
473
- noneOfTheAboveIndex,
474
- noneOfTheAboveSelected,
475
- };
476
- // Support legacy choiceState implementation
477
- }
478
- /* istanbul ignore if - props.values is deprecated */
479
- const {values} = props;
480
- if (values) {
481
- let noneOfTheAboveIndex = null;
482
- let noneOfTheAboveSelected = false;
483
-
484
- const choicesSelected = [...values];
485
- const countChoices = props.countChoices;
486
- const numCorrect = props.numCorrect;
487
- const valuesLength = values.length;
488
-
489
- for (let i = 0; i < valuesLength; i++) {
490
- const index = props.choices[i].originalIndex;
491
- choicesSelected[index] = values[i];
492
-
493
- if (props.choices[i].isNoneOfTheAbove) {
494
- noneOfTheAboveIndex = index;
495
- if (choicesSelected[i]) {
496
- noneOfTheAboveSelected = true;
497
- }
498
- }
499
- }
500
- return {
501
- choicesSelected,
502
- noneOfTheAboveIndex,
503
- noneOfTheAboveSelected,
504
- countChoices,
505
- numCorrect,
506
- };
507
- }
508
- // Nothing checked
509
- return {
510
- choicesSelected: props.choices.map(() => false),
511
- };
512
- }
513
512
  }
514
513
 
515
514
  export default Radio;
@@ -1,9 +0,0 @@
1
- // @flow
2
- import $ from "jquery";
3
-
4
- export const getLineHeightForNode = (
5
- line1: HTMLDivElement,
6
- line2: HTMLDivElement,
7
- ): number => {
8
- return $(line2).offset().top - $(line1).offset().top;
9
- };