@khanacademy/perseus 33.3.0 → 34.0.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.
Files changed (36) hide show
  1. package/dist/components/sortable.d.ts +1 -4
  2. package/dist/es/index.js +448 -540
  3. package/dist/es/index.js.map +1 -1
  4. package/dist/index.d.ts +0 -1
  5. package/dist/index.js +444 -542
  6. package/dist/index.js.map +1 -1
  7. package/dist/perseus-api.d.ts +1 -0
  8. package/dist/perseus-types.d.ts +2 -1
  9. package/dist/server-item-renderer.d.ts +1 -0
  10. package/dist/types.d.ts +1 -1
  11. package/dist/util/answer-types.d.ts +1 -1
  12. package/dist/widgets/__shared__/noop-validator.d.ts +11 -0
  13. package/dist/widgets/categorizer/categorizer.d.ts +3 -1
  14. package/dist/widgets/cs-program/cs-program.d.ts +23 -17
  15. package/dist/widgets/dropdown/dropdown-validator.d.ts +4 -0
  16. package/dist/widgets/dropdown/dropdown.d.ts +1 -5
  17. package/dist/widgets/dropdown/dropdown.types.d.ts +5 -0
  18. package/dist/widgets/expression/expression-validator.d.ts +5 -0
  19. package/dist/widgets/expression/expression.d.ts +7 -6
  20. package/dist/widgets/expression/expression.types.d.ts +3 -0
  21. package/dist/widgets/expression/get-decimal-separator.d.ts +5 -0
  22. package/dist/widgets/iframe/iframe.d.ts +24 -15
  23. package/dist/widgets/interactive-graphs/graph-locked-labels-layer.d.ts +2 -0
  24. package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +5 -0
  25. package/dist/widgets/interactive-graphs/mafs-graph.d.ts +2 -1
  26. package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +2 -1
  27. package/dist/widgets/label-image/label-image.d.ts +2 -0
  28. package/dist/widgets/matcher/matcher.d.ts +17 -46
  29. package/dist/widgets/numeric-input/numeric-input-validator.d.ts +6 -0
  30. package/dist/widgets/numeric-input/numeric-input.d.ts +1 -5
  31. package/dist/widgets/numeric-input/numeric-input.types.d.ts +5 -0
  32. package/dist/widgets/orderer/orderer.d.ts +6 -5
  33. package/dist/widgets/sorter/sorter.d.ts +18 -44
  34. package/dist/widgets/table/table.d.ts +20 -24
  35. package/package.json +1 -1
  36. package/dist/mixins/widget-jsonify-deprecated.d.ts +0 -5
package/dist/es/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
2
  import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
3
- import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
4
3
  import * as PerseusLinter from '@khanacademy/perseus-linter';
5
- import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
4
+ import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
6
5
  import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
7
6
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
8
7
  import { HeadingMedium, LabelSmall, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
@@ -18,6 +17,7 @@ import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder
18
17
  import { color, spacing, font } from '@khanacademy/wonder-blocks-tokens';
19
18
  import $ from 'jquery';
20
19
  import { v4 } from 'uuid';
20
+ import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
21
21
  import PropTypes from 'prop-types';
22
22
  import { number, point, vector as vector$1, line } from '@khanacademy/kmath';
23
23
  import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
@@ -732,23 +732,6 @@ var dependencies = /*#__PURE__*/Object.freeze({
732
732
  useDependencies: useDependencies
733
733
  });
734
734
 
735
- // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
736
- // getDependencies() internally... simplify usage to just using a "singleton"
737
- // which get's the injected logger.
738
- /**
739
- * Provides logging infrastructure for Perseus
740
- */
741
- const Log = {
742
- // Logs a message
743
- log: (message, extra) => {
744
- getDependencies().Log.log(message, extra);
745
- },
746
- // Logs an error with a message
747
- error: (message, kind, extra) => {
748
- getDependencies().Log.error(message, kind, extra);
749
- }
750
- };
751
-
752
735
  /**
753
736
  * These are things that widgets should exclude when serializing themselves.
754
737
  *
@@ -1192,6 +1175,23 @@ const ClassNames = {
1192
1175
  MOBILE: "perseus-mobile"
1193
1176
  };
1194
1177
 
1178
+ // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
1179
+ // getDependencies() internally... simplify usage to just using a "singleton"
1180
+ // which get's the injected logger.
1181
+ /**
1182
+ * Provides logging infrastructure for Perseus
1183
+ */
1184
+ const Log = {
1185
+ // Logs a message
1186
+ log: (message, extra) => {
1187
+ getDependencies().Log.log(message, extra);
1188
+ },
1189
+ // Logs an error with a message
1190
+ error: (message, kind, extra) => {
1191
+ getDependencies().Log.error(message, kind, extra);
1192
+ }
1193
+ };
1194
+
1195
1195
  const KhanMath = {
1196
1196
  // Simplify formulas before display
1197
1197
  cleanMath: function (expr) {
@@ -2106,6 +2106,176 @@ const KhanAnswerTypes = {
2106
2106
  }
2107
2107
  };
2108
2108
 
2109
+ /**
2110
+ * Get the character used for separating decimals.
2111
+ */
2112
+ const getDecimalSeparator = locale => {
2113
+ var _match$;
2114
+ switch (locale) {
2115
+ // TODO(somewhatabstract): Remove this when Chrome supports the `ka`
2116
+ // locale properly.
2117
+ // https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
2118
+ //
2119
+ // Supported locales in Chrome:
2120
+ // https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
2121
+ case "ka":
2122
+ return ",";
2123
+ default:
2124
+ const numberWithDecimalSeparator = 1.1;
2125
+ // TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
2126
+ // support Safari 12.
2127
+ const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
2128
+ // 0x661 is ARABIC-INDIC DIGIT ONE
2129
+ // 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
2130
+ .match(/[^\d\u0661\u06F1]/);
2131
+ return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
2132
+ }
2133
+ };
2134
+
2135
+ /* Content creators input a list of answers which are matched from top to
2136
+ * bottom. The intent is that they can include spcific solutions which should
2137
+ * be graded as correct or incorrect (or ungraded!) first, then get more
2138
+ * general.
2139
+ *
2140
+ * We iterate through each answer, trying to match it with the user's input
2141
+ * using the following angorithm:
2142
+ * - Try to parse the user's input. If it doesn't parse then return "not
2143
+ * graded".
2144
+ * - For each answer:
2145
+ * ~ Try to validate the user's input against the answer. The answer is
2146
+ * expected to parse.
2147
+ * ~ If the user's input validates (the validator judges it "correct"), we've
2148
+ * matched and can stop considering answers.
2149
+ * - If there were no matches or the matching answer is considered "ungraded",
2150
+ * show the user an error. TODO(joel) - what error?
2151
+ * - Otherwise, pass through the resulting points and message.
2152
+ */
2153
+ function expressionValidator(userInput, rubric,
2154
+ // @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
2155
+ onInputError = function () {}, strings, locale) {
2156
+ const options = _.clone(rubric);
2157
+ _.extend(options, {
2158
+ decimal_separator: getDecimalSeparator(locale)
2159
+ });
2160
+ const createValidator = answer => {
2161
+ // We give options to KAS.parse here because it is parsing the
2162
+ // solution answer, not the student answer, and we don't want a
2163
+ // solution to work if the student is using a different language
2164
+ // (different from the content creation language, ie. English).
2165
+ const expression = KAS.parse(answer.value, rubric);
2166
+ // An answer may not be parsed if the expression was defined
2167
+ // incorrectly. For example if the answer is using a symbol defined
2168
+ // in the function variables list for the expression.
2169
+ if (!expression.parsed) {
2170
+ /* c8 ignore next */
2171
+ Log.error("Unable to parse solution answer for expression", Errors.InvalidInput, {
2172
+ loggedMetadata: {
2173
+ rubric: JSON.stringify(rubric)
2174
+ }
2175
+ });
2176
+ return null;
2177
+ }
2178
+ return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
2179
+ simplify: answer.simplify,
2180
+ form: answer.form
2181
+ }), strings);
2182
+ };
2183
+
2184
+ // Find the first answer form that matches the user's input and that
2185
+ // is considered correct. Also, track whether the input is
2186
+ // considered "empty" for all answer forms, and keep the validation
2187
+ // result for the first answer form for which the user's input was
2188
+ // considered "ungraded".
2189
+ // (Terminology reminder: the answer forms are provided by the
2190
+ // assessment items; they are not the user's input. Each one might
2191
+ // represent a correct answer, an incorrect one (if the exercise
2192
+ // creator has predicted certain common wrong answers and wants to
2193
+ // provide guidance via a message), or an ungraded one (same idea,
2194
+ // but without giving the user an incorrect mark for the question).
2195
+ let matchingAnswerForm;
2196
+ let matchMessage;
2197
+ let allEmpty = true;
2198
+ let firstUngradedResult;
2199
+ for (const answerForm of rubric.answerForms || []) {
2200
+ const validator = createValidator(answerForm);
2201
+ if (!validator) {
2202
+ continue;
2203
+ }
2204
+ const result = validator(userInput);
2205
+
2206
+ // Short-circuit as soon as the user's input matches some answer
2207
+ // (independently of whether the answer is correct)
2208
+ if (result.correct) {
2209
+ matchingAnswerForm = answerForm;
2210
+ matchMessage = result.message || "";
2211
+ break;
2212
+ }
2213
+ allEmpty = allEmpty && result.empty;
2214
+ // If this answer form is correct and the user's input is considered
2215
+ // "ungraded" for it, we'll want to keep the evaluation result for
2216
+ // later. If the user's input doesn't match any answer forms, we'll
2217
+ // show the message from this validation.
2218
+ if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
2219
+ firstUngradedResult = result;
2220
+ }
2221
+ }
2222
+
2223
+ // Now check to see if we matched any answer form at all, and if
2224
+ // we did, whether it's considered correct, incorrect, or ungraded
2225
+ if (!matchingAnswerForm) {
2226
+ if (firstUngradedResult) {
2227
+ // While we didn't directly match with any answer form, we
2228
+ // did at some point get an "ungraded" validation result,
2229
+ // which might indicate e.g. a mismatch in variable casing.
2230
+ // We'll return "invalid", which will let the user try again
2231
+ // with no penalty, and the hopefully helpful validation
2232
+ // message.
2233
+ return {
2234
+ type: "invalid",
2235
+ message: firstUngradedResult.message,
2236
+ suppressAlmostThere: firstUngradedResult.suppressAlmostThere
2237
+ };
2238
+ }
2239
+ if (allEmpty) {
2240
+ // If everything graded as empty, it's invalid.
2241
+ return {
2242
+ type: "invalid",
2243
+ message: null
2244
+ };
2245
+ }
2246
+ // We fell through all the possibilities and we're not empty,
2247
+ // so the answer is considered incorrect.
2248
+ return {
2249
+ type: "points",
2250
+ earned: 0,
2251
+ total: 1
2252
+ };
2253
+ }
2254
+ if (matchingAnswerForm.considered === "ungraded") {
2255
+ // We matched an ungraded answer form - return "invalid", which
2256
+ // will let the user try again with no penalty
2257
+ const apiResult = onInputError(null,
2258
+ // Reserved for some widget identifier
2259
+ userInput, matchMessage);
2260
+ return {
2261
+ type: "invalid",
2262
+ message: apiResult === false ? null : matchMessage
2263
+ };
2264
+ }
2265
+ // We matched a graded answer form, so we can now tell the user
2266
+ // whether their input was correct or incorrect, and hand out
2267
+ // points accordingly
2268
+ // TODO(eater): Seems silly to translate result to this
2269
+ // invalid/points thing and immediately translate it back in
2270
+ // ItemRenderer.scoreInput()
2271
+ return {
2272
+ type: "points",
2273
+ earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
2274
+ total: 1,
2275
+ message: matchMessage
2276
+ };
2277
+ }
2278
+
2109
2279
  const _excluded$5 = ["scoring"];
2110
2280
  // Map of international operator names to their English equivalents
2111
2281
  const englishOperators = {
@@ -2136,32 +2306,6 @@ const deriveKeypadVersion = apiOptions => {
2136
2306
  // can remove this `virtualKeypadVersion` field entirely.
2137
2307
  return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : "MATH_INPUT_KEYPAD_V2";
2138
2308
  };
2139
- /**
2140
- * Get the character used for separating decimals.
2141
- */
2142
- const getDecimalSeparator = locale => {
2143
- var _match$;
2144
- switch (locale) {
2145
- // TODO(somewhatabstract): Remove this when Chrome supports the `ka`
2146
- // locale properly.
2147
- // https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
2148
- //
2149
- // Supported locales in Chrome:
2150
- // https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
2151
- case "ka":
2152
- return ",";
2153
- default:
2154
- const numberWithDecimalSeparator = 1.1;
2155
- // TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
2156
- // support Safari 12.
2157
- const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
2158
- // 0x661 is ARABIC-INDIC DIGIT ONE
2159
- // 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
2160
- .match(/[^\d\u0661\u06F1]/);
2161
- return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
2162
- }
2163
- };
2164
-
2165
2309
  // The new, MathQuill input expression widget
2166
2310
  class Expression extends React.Component {
2167
2311
  constructor(..._args) {
@@ -2230,7 +2374,7 @@ class Expression extends React.Component {
2230
2374
  scoring
2231
2375
  } = _ref,
2232
2376
  rubric = _objectWithoutPropertiesLoose(_ref, _excluded$5);
2233
- const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
2377
+ const score = expressionValidator(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
2234
2378
 
2235
2379
  // "scoring" is a flag that indicates when we are checking answers.
2236
2380
  // otherwise, we may just be checking validity after changes.
@@ -2320,149 +2464,11 @@ class Expression extends React.Component {
2320
2464
  }, cb);
2321
2465
  };
2322
2466
  }
2323
- //#region Previously a class extension
2324
- /* Content creators input a list of answers which are matched from top to
2325
- * bottom. The intent is that they can include spcific solutions which should
2326
- * be graded as correct or incorrect (or ungraded!) first, then get more
2327
- * general.
2328
- *
2329
- * We iterate through each answer, trying to match it with the user's input
2330
- * using the following angorithm:
2331
- * - Try to parse the user's input. If it doesn't parse then return "not
2332
- * graded".
2333
- * - For each answer:
2334
- * ~ Try to validate the user's input against the answer. The answer is
2335
- * expected to parse.
2336
- * ~ If the user's input validates (the validator judges it "correct"), we've
2337
- * matched and can stop considering answers.
2338
- * - If there were no matches or the matching answer is considered "ungraded",
2339
- * show the user an error. TODO(joel) - what error?
2340
- * - Otherwise, pass through the resulting points and message.
2341
- */
2467
+ // TODO remove this in favor of just using expressionValidator
2342
2468
  static validate(userInput, rubric,
2343
2469
  // @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
2344
2470
  onInputError = function () {}, strings, locale) {
2345
- const options = _.clone(rubric);
2346
- _.extend(options, {
2347
- decimal_separator: getDecimalSeparator(locale)
2348
- });
2349
- const createValidator = answer => {
2350
- // We give options to KAS.parse here because it is parsing the
2351
- // solution answer, not the student answer, and we don't want a
2352
- // solution to work if the student is using a different language
2353
- // (different from the content creation language, ie. English).
2354
- const expression = KAS.parse(answer.value, rubric);
2355
- // An answer may not be parsed if the expression was defined
2356
- // incorrectly. For example if the answer is using a symbol defined
2357
- // in the function variables list for the expression.
2358
- if (!expression.parsed) {
2359
- /* c8 ignore next */
2360
- Log.error("Unable to parse solution answer for expression", Errors.InvalidInput, {
2361
- loggedMetadata: {
2362
- rubric: JSON.stringify(rubric)
2363
- }
2364
- });
2365
- return null;
2366
- }
2367
- return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
2368
- simplify: answer.simplify,
2369
- form: answer.form
2370
- }), strings);
2371
- };
2372
-
2373
- // Find the first answer form that matches the user's input and that
2374
- // is considered correct. Also, track whether the input is
2375
- // considered "empty" for all answer forms, and keep the validation
2376
- // result for the first answer form for which the user's input was
2377
- // considered "ungraded".
2378
- // (Terminology reminder: the answer forms are provided by the
2379
- // assessment items; they are not the user's input. Each one might
2380
- // represent a correct answer, an incorrect one (if the exercise
2381
- // creator has predicted certain common wrong answers and wants to
2382
- // provide guidance via a message), or an ungraded one (same idea,
2383
- // but without giving the user an incorrect mark for the question).
2384
- let matchingAnswerForm;
2385
- let matchMessage;
2386
- let allEmpty = true;
2387
- let firstUngradedResult;
2388
- for (const answerForm of rubric.answerForms || []) {
2389
- const validate = createValidator(answerForm);
2390
- if (!validate) {
2391
- continue;
2392
- }
2393
- const result = validate(userInput);
2394
-
2395
- // Short-circuit as soon as the user's input matches some answer
2396
- // (independently of whether the answer is correct)
2397
- if (result.correct) {
2398
- matchingAnswerForm = answerForm;
2399
- matchMessage = result.message || "";
2400
- break;
2401
- }
2402
- allEmpty = allEmpty && result.empty;
2403
- // If this answer form is correct and the user's input is considered
2404
- // "ungraded" for it, we'll want to keep the evaluation result for
2405
- // later. If the user's input doesn't match any answer forms, we'll
2406
- // show the message from this validation.
2407
- if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
2408
- firstUngradedResult = result;
2409
- }
2410
- }
2411
-
2412
- // Now check to see if we matched any answer form at all, and if
2413
- // we did, whether it's considered correct, incorrect, or ungraded
2414
- if (!matchingAnswerForm) {
2415
- if (firstUngradedResult) {
2416
- // While we didn't directly match with any answer form, we
2417
- // did at some point get an "ungraded" validation result,
2418
- // which might indicate e.g. a mismatch in variable casing.
2419
- // We'll return "invalid", which will let the user try again
2420
- // with no penalty, and the hopefully helpful validation
2421
- // message.
2422
- return {
2423
- type: "invalid",
2424
- message: firstUngradedResult.message,
2425
- suppressAlmostThere: firstUngradedResult.suppressAlmostThere
2426
- };
2427
- }
2428
- if (allEmpty) {
2429
- // If everything graded as empty, it's invalid.
2430
- return {
2431
- type: "invalid",
2432
- message: null
2433
- };
2434
- }
2435
- // We fell through all the possibilities and we're not empty,
2436
- // so the answer is considered incorrect.
2437
- return {
2438
- type: "points",
2439
- earned: 0,
2440
- total: 1
2441
- };
2442
- }
2443
- if (matchingAnswerForm.considered === "ungraded") {
2444
- // We matched an ungraded answer form - return "invalid", which
2445
- // will let the user try again with no penalty
2446
- const apiResult = onInputError(null,
2447
- // Reserved for some widget identifier
2448
- userInput, matchMessage);
2449
- return {
2450
- type: "invalid",
2451
- message: apiResult === false ? null : matchMessage
2452
- };
2453
- }
2454
- // We matched a graded answer form, so we can now tell the user
2455
- // whether their input was correct or incorrect, and hand out
2456
- // points accordingly
2457
- // TODO(eater): Seems silly to translate result to this
2458
- // invalid/points thing and immediately translate it back in
2459
- // ItemRenderer.scoreInput()
2460
- return {
2461
- type: "points",
2462
- earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
2463
- total: 1,
2464
- message: matchMessage
2465
- };
2471
+ return expressionValidator(userInput, rubric, onInputError, strings, locale);
2466
2472
  }
2467
2473
  static getUserInputFromProps(props) {
2468
2474
  return normalizeTex(props.value);
@@ -2670,10 +2676,10 @@ const ExpressionWithDependencies = /*#__PURE__*/React.forwardRef((props, ref) =>
2670
2676
 
2671
2677
  // HACK: Propogate "static" methods onto our wrapper component.
2672
2678
  // In the future we should adjust client apps to not depend on these static
2673
- // methods and instead adjust Peresus to provide these facilities through
2679
+ // methods and instead adjust Perseus to provide these facilities through
2674
2680
  // instance methods on our Renderers.
2675
2681
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2676
- ExpressionWithDependencies.validate = Expression.validate;
2682
+ ExpressionWithDependencies.validate = expressionValidator;
2677
2683
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2678
2684
  ExpressionWithDependencies.getUserInputFromProps = Expression.getUserInputFromProps;
2679
2685
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
@@ -19620,9 +19626,7 @@ const isIdPathPrefix = function isIdPathPrefix(prefixArray, wholeArray) {
19620
19626
 
19621
19627
  // The return type for getUserInput. Widgets have full control of what is
19622
19628
  // returned so it's not easily typed (some widgets return a scalar (string),
19623
- // some return a custom-built object, some delegate to
19624
- // `WidgetJsonifyDeprecated` which returns an object containing widget props
19625
- // (filtered by deprecated keys)).
19629
+ // some return a custom-built object
19626
19630
  /**
19627
19631
  * The Widget type represents the common API that the Renderer uses to interact
19628
19632
  * with all widgets. All widgets must implement the methods in this API, unless
@@ -22024,6 +22028,131 @@ const answerFormButtons = [{
22024
22028
  value: "pi",
22025
22029
  content: "\u03C0"
22026
22030
  }];
22031
+
22032
+ // This function checks if the user inputted a percent value, parsing
22033
+ // it as a number (and maybe scaling) so that it can be graded.
22034
+ // NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
22035
+ // can accept several input forms with or without "%", the decision
22036
+ // to parse based on the presence of "%" in the input, is so that we
22037
+ // don't accidently scale the user typed value before grading, CP-930.
22038
+ function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
22039
+ // If the input value is not a string ending with "%", then there's
22040
+ // nothing more to do. The value will be graded as inputted by user.
22041
+ if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
22042
+ return inputValue;
22043
+ }
22044
+ const value = parseFloat(inputValue.slice(0, -1));
22045
+ // If the input value stripped of the "%" cannot be parsed as a
22046
+ // number (the slice is not really necessary for parseFloat to work
22047
+ // if the string starts with a number) then return the original
22048
+ // input for grading.
22049
+ if (isNaN(value)) {
22050
+ return inputValue;
22051
+ }
22052
+
22053
+ // Next, if all correct answers are in the range of |0,1| then we
22054
+ // scale the user typed value. We assume this is the correct thing
22055
+ // to do since the input value ends with "%".
22056
+ if (normalizedAnswerExpected) {
22057
+ return value / 100;
22058
+ }
22059
+
22060
+ // Otherwise, we return input valuåe (number) stripped of the "%".
22061
+ return value;
22062
+ }
22063
+ function numericInputValidator(userInput, rubric, strings) {
22064
+ const defaultAnswerForms = answerFormButtons.map(e => e["value"])
22065
+ // Don't default to validating the answer as a pi answer
22066
+ // if answerForm isn't set on the answer
22067
+ // https://khanacademy.atlassian.net/browse/LC-691
22068
+ .filter(e => e !== "pi");
22069
+ const createValidator = answer => {
22070
+ var _answer$answerForms;
22071
+ const stringAnswer = `${answer.value}`;
22072
+
22073
+ // Always validate against the provided answer forms (pi, decimal, etc.)
22074
+ const validatorForms = [...((_answer$answerForms = answer.answerForms) != null ? _answer$answerForms : [])];
22075
+
22076
+ // When an answer is set to strict, we validate using ONLY
22077
+ // the provided answerForms. If strict is false, or if there
22078
+ // were no provided answer forms, we will include all
22079
+ // of the default answer forms in our validator.
22080
+ if (!answer.strict || validatorForms.length === 0) {
22081
+ validatorForms.push(...defaultAnswerForms);
22082
+ }
22083
+ return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
22084
+ message: answer.message,
22085
+ simplify: answer.status === "correct" ? answer.simplify : "optional",
22086
+ inexact: true,
22087
+ // TODO(merlob) backfill / delete
22088
+ maxError: answer.maxError,
22089
+ forms: validatorForms
22090
+ }, strings);
22091
+ };
22092
+
22093
+ // We may have received TeX; try to parse it before grading.
22094
+ // If `currentValue` is not TeX, this should be a no-op.
22095
+ const currentValue = ParseTex(userInput.currentValue);
22096
+ const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22097
+ const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
22098
+
22099
+ // Look through all correct answers for one that matches either
22100
+ // precisely or approximately and return the appropriate message:
22101
+ // - if precise, return the message that the answer came with
22102
+ // - if it needs to be simplified, etc., show that message
22103
+ let result = correctAnswers.map(answer => {
22104
+ // The coefficient is an attribute of the widget
22105
+ let localValue = currentValue;
22106
+ if (rubric.coefficient) {
22107
+ if (!localValue) {
22108
+ localValue = 1;
22109
+ } else if (localValue === "-") {
22110
+ localValue = -1;
22111
+ }
22112
+ }
22113
+ const validate = createValidator(answer);
22114
+ return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
22115
+ }).find(match => match.correct || match.empty);
22116
+ if (!result) {
22117
+ // Otherwise, if the guess is not correct
22118
+ const otherAnswers = [].concat(
22119
+ // @ts-expect-error - TS2769 - No overload matches this call.
22120
+ rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
22121
+
22122
+ // Look through all other answers and if one matches either
22123
+ // precisely or approximately return the answer's message
22124
+ const match = otherAnswers.find(answer => {
22125
+ const validate = createValidator(answer);
22126
+ return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
22127
+ });
22128
+ result = {
22129
+ // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22130
+ empty: match ? match.status === "ungraded" : false,
22131
+ // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22132
+ correct: match ? match.status === "correct" : false,
22133
+ // @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
22134
+ message: match ? match.message : null,
22135
+ guess: currentValue
22136
+ };
22137
+ }
22138
+
22139
+ // TODO(eater): Seems silly to translate result to this
22140
+ // invalid/points thing and immediately translate it
22141
+ // back in ItemRenderer.scoreInput()
22142
+ if (result.empty) {
22143
+ return {
22144
+ type: "invalid",
22145
+ message: result.message
22146
+ };
22147
+ }
22148
+ return {
22149
+ type: "points",
22150
+ earned: result.correct ? 1 : 0,
22151
+ total: 1,
22152
+ message: result.message
22153
+ };
22154
+ }
22155
+
22027
22156
  const formExamples = {
22028
22157
  integer: (form, strings) => strings.integerExample,
22029
22158
  proper: (form, strings) => form.simplify === "optional" ? strings.properExample : strings.simplifiedProperExample,
@@ -22070,7 +22199,7 @@ class NumericInput extends React.Component {
22070
22199
  return !noFormsAccepted && !allFormsAccepted;
22071
22200
  };
22072
22201
  this.simpleValidate = rubric => {
22073
- return NumericInput.validate(this.getUserInput(), rubric, this.context.strings);
22202
+ return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
22074
22203
  };
22075
22204
  this.focus = () => {
22076
22205
  var _this$inputRef;
@@ -22151,98 +22280,7 @@ class NumericInput extends React.Component {
22151
22280
  return answerStrings[0];
22152
22281
  }
22153
22282
  static validate(userInput, rubric, strings) {
22154
- const defaultAnswerForms = answerFormButtons.map(e => e["value"])
22155
- // Don't default to validating the answer as a pi answer
22156
- // if answerForm isn't set on the answer
22157
- // https://khanacademy.atlassian.net/browse/LC-691
22158
- .filter(e => e !== "pi");
22159
- const createValidator = answer => {
22160
- var _answer$answerForms;
22161
- const stringAnswer = `${answer.value}`;
22162
-
22163
- // Always validate against the provided answer forms (pi, decimal, etc.)
22164
- const validatorForms = [...((_answer$answerForms = answer.answerForms) != null ? _answer$answerForms : [])];
22165
-
22166
- // When an answer is set to strict, we validate using ONLY
22167
- // the provided answerForms. If strict is false, or if there
22168
- // were no provided answer forms, we will include all
22169
- // of the default answer forms in our validator.
22170
- if (!answer.strict || validatorForms.length === 0) {
22171
- validatorForms.push(...defaultAnswerForms);
22172
- }
22173
- return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
22174
- message: answer.message,
22175
- simplify: answer.status === "correct" ? answer.simplify : "optional",
22176
- inexact: true,
22177
- // TODO(merlob) backfill / delete
22178
- maxError: answer.maxError,
22179
- forms: validatorForms
22180
- }, strings);
22181
- };
22182
-
22183
- // We may have received TeX; try to parse it before grading.
22184
- // If `currentValue` is not TeX, this should be a no-op.
22185
- const currentValue = ParseTex(userInput.currentValue);
22186
- const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22187
- const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
22188
-
22189
- // Look through all correct answers for one that matches either
22190
- // precisely or approximately and return the appropriate message:
22191
- // - if precise, return the message that the answer came with
22192
- // - if it needs to be simplified, etc., show that message
22193
- let result = correctAnswers.map(answer => {
22194
- // The coefficient is an attribute of the widget
22195
- let localValue = currentValue;
22196
- if (rubric.coefficient) {
22197
- if (!localValue) {
22198
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
22199
- localValue = 1;
22200
- } else if (localValue === "-") {
22201
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
22202
- localValue = -1;
22203
- }
22204
- }
22205
- const validate = createValidator(answer);
22206
- return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
22207
- }).find(match => match.correct || match.empty);
22208
- if (!result) {
22209
- // Otherwise, if the guess is not correct
22210
- const otherAnswers = [].concat(
22211
- // @ts-expect-error - TS2769 - No overload matches this call.
22212
- rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
22213
-
22214
- // Look through all other answers and if one matches either
22215
- // precisely or approximately return the answer's message
22216
- const match = otherAnswers.find(answer => {
22217
- const validate = createValidator(answer);
22218
- return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
22219
- });
22220
- result = {
22221
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22222
- empty: match ? match.status === "ungraded" : false,
22223
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22224
- correct: match ? match.status === "correct" : false,
22225
- // @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
22226
- message: match ? match.message : null,
22227
- guess: currentValue
22228
- };
22229
- }
22230
-
22231
- // TODO(eater): Seems silly to translate result to this
22232
- // invalid/points thing and immediately translate it
22233
- // back in ItemRenderer.scoreInput()
22234
- if (result.empty) {
22235
- return {
22236
- type: "invalid",
22237
- message: result.message
22238
- };
22239
- }
22240
- return {
22241
- type: "points",
22242
- earned: result.correct ? 1 : 0,
22243
- total: 1,
22244
- message: result.message
22245
- };
22283
+ return numericInputValidator(userInput, rubric, strings);
22246
22284
  }
22247
22285
  render() {
22248
22286
  let labelText = this.props.labelText;
@@ -22343,37 +22381,6 @@ const unionAnswerForms = function unionAnswerForms(answerFormsList) {
22343
22381
  return formExampleKeys.indexOf(form.name);
22344
22382
  });
22345
22383
  };
22346
- // This function checks if the user inputted a percent value, parsing
22347
- // it as a number (and maybe scaling) so that it can be graded.
22348
- // NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
22349
- // can accept several input forms with or without "%", the decision
22350
- // to parse based on the presence of "%" in the input, is so that we
22351
- // don't accidently scale the user typed value before grading, CP-930.
22352
- function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
22353
- // If the input value is not a string ending with "%", then there's
22354
- // nothing more to do. The value will be graded as inputted by user.
22355
- if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
22356
- return inputValue;
22357
- }
22358
- const value = parseFloat(inputValue.slice(0, -1));
22359
- // If the input value stripped of the "%" cannot be parsed as a
22360
- // number (the slice is not really necessary for parseFloat to work
22361
- // if the string starts with a number) then return the original
22362
- // input for grading.
22363
- if (isNaN(value)) {
22364
- return inputValue;
22365
- }
22366
-
22367
- // Next, if all correct answers are in the range of |0,1| then we
22368
- // scale the user typed value. We assume this is the correct thing
22369
- // to do since the input value ends with "%".
22370
- if (normalizedAnswerExpected) {
22371
- return value / 100;
22372
- }
22373
-
22374
- // Otherwise, we return input valuåe (number) stripped of the "%".
22375
- return value;
22376
- }
22377
22384
  const propsTransform = function propsTransform(widgetOptions) {
22378
22385
  const rendererProps = _.extend(_.omit(widgetOptions, "answers"), {
22379
22386
  answerForms: unionAnswerForms(
@@ -22401,6 +22408,23 @@ var NumericInput$1 = {
22401
22408
  isLintable: true
22402
22409
  };
22403
22410
 
22411
+ /**
22412
+ * Several widgets don't have "right"/"wrong" validation logic,
22413
+ * so this just says to move on past those widgets
22414
+ *
22415
+ * TODO: widgets that use this probably shouldn't have any
22416
+ * validation logic and the thing validating an exercise
22417
+ * should just know to skip these
22418
+ */
22419
+ function noopValidator() {
22420
+ return {
22421
+ type: "points",
22422
+ earned: 0,
22423
+ total: 0,
22424
+ message: null
22425
+ };
22426
+ }
22427
+
22404
22428
  const EN_DASH$1 = "\u2013";
22405
22429
  class PassageRef extends React.Component {
22406
22430
  constructor(..._args) {
@@ -22453,12 +22477,7 @@ class PassageRef extends React.Component {
22453
22477
  };
22454
22478
  }
22455
22479
  static validate(userInput, rubric) {
22456
- return {
22457
- type: "points",
22458
- earned: 0,
22459
- total: 0,
22460
- message: null
22461
- };
22480
+ return noopValidator();
22462
22481
  }
22463
22482
  componentDidMount() {
22464
22483
  // TODO(scottgrant): This is a hack to remove the deprecated call to
@@ -22521,6 +22540,7 @@ PassageRef.defaultProps = {
22521
22540
  var PassageRef$1 = {
22522
22541
  name: "passage-ref",
22523
22542
  displayName: "PassageRef (SAT only)",
22543
+ hidden: true,
22524
22544
  defaultAlignment: "inline",
22525
22545
  widget: PassageRef,
22526
22546
  transform: widgetOptions => ({
@@ -24406,23 +24426,6 @@ var Radio = {
24406
24426
  /* eslint-disable import/no-named-as-default */
24407
24427
  var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
24408
24428
 
24409
- /* Free implementation of getUserInput. This should be used sparingly, since it
24410
- * just returns all the widget's props rather than picking out those which were
24411
- * input by the user.
24412
- */
24413
- const WidgetJsonifyDeprecated = {
24414
- getUserInput: function () {
24415
- // Omit props that get passed to all widgets
24416
- // @ts-expect-error - TS2339 - Property 'props' does not exist on type '{ readonly getUserInput: () => any; readonly getUserInputFromProps: (props: any) => any; }'.
24417
- return _.omit(this.props, denylist);
24418
- },
24419
- // Static version of `WidgetJsonifyDeprecated.getUserInput`
24420
- getUserInputFromProps: function (props) {
24421
- // Omit props that get passed to all widgets
24422
- return _.omit(props, denylist);
24423
- }
24424
- };
24425
-
24426
24429
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
24427
24430
  class Categorizer extends React.Component {
24428
24431
  constructor(..._args) {
@@ -24466,7 +24469,9 @@ class Categorizer extends React.Component {
24466
24469
  };
24467
24470
  }
24468
24471
  static getUserInputFromProps(props) {
24469
- return WidgetJsonifyDeprecated.getUserInputFromProps(props);
24472
+ return {
24473
+ values: props.values
24474
+ };
24470
24475
  }
24471
24476
  onChange(itemNum, catNum) {
24472
24477
  const values = [...this.props.values];
@@ -24612,6 +24617,7 @@ const styles$s = StyleSheet.create({
24612
24617
  var Categorizer$1 = {
24613
24618
  name: "categorizer",
24614
24619
  displayName: "Categorizer",
24620
+ hidden: true,
24615
24621
  widget: Categorizer,
24616
24622
  transform: widgetOptions => {
24617
24623
  return _.pick(widgetOptions, "items", "categories", "randomizeItems");
@@ -24669,6 +24675,9 @@ const parseUrl = url => new URL(url, location().origin);
24669
24675
  */
24670
24676
  const toAbsoluteUrl = url => parseUrl(url).href;
24671
24677
 
24678
+ /**
24679
+ * This widget is for embedding Khan Academy CS programs.
24680
+ */
24672
24681
  const {
24673
24682
  updateQueryString: updateQueryString$1
24674
24683
  } = Util;
@@ -24714,12 +24723,6 @@ class CSProgram extends React.Component {
24714
24723
  // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
24715
24724
  return change.apply(this, args);
24716
24725
  };
24717
- this.simpleValidate = rubric => {
24718
- return CSProgram.validate({
24719
- status: this.props.status,
24720
- message: this.props.message
24721
- }, rubric);
24722
- };
24723
24726
  }
24724
24727
  // The widget's grading function
24725
24728
  static validate(state, rubric) {
@@ -24752,6 +24755,12 @@ class CSProgram extends React.Component {
24752
24755
  componentWillUnmount() {
24753
24756
  $(window).off("message", this.handleMessageEvent);
24754
24757
  }
24758
+ simpleValidate(rubric) {
24759
+ return CSProgram.validate({
24760
+ status: this.props.status,
24761
+ message: this.props.message
24762
+ }, rubric);
24763
+ }
24755
24764
  render() {
24756
24765
  if (!this.props.programID) {
24757
24766
  return /*#__PURE__*/React.createElement("div", null);
@@ -24811,18 +24820,6 @@ class CSProgram extends React.Component {
24811
24820
  }));
24812
24821
  }
24813
24822
  }
24814
- CSProgram.propTypes = _extends({}, propTypes, {
24815
- programID: PropTypes.string,
24816
- programType: PropTypes.oneOf(["pjs", "sql", "webpage"]),
24817
- width: PropTypes.number,
24818
- height: PropTypes.number,
24819
- // eslint-disable-next-line react/forbid-prop-types
24820
- settings: PropTypes.array,
24821
- showEditor: PropTypes.bool,
24822
- showButtons: PropTypes.bool,
24823
- status: PropTypes.oneOf(["incomplete", "incorrect", "correct"]),
24824
- message: PropTypes.string
24825
- });
24826
24823
  CSProgram.defaultProps = {
24827
24824
  showEditor: false,
24828
24825
  showButtons: false,
@@ -24859,12 +24856,7 @@ class Definition extends React.Component {
24859
24856
  };
24860
24857
  }
24861
24858
  static validate(userInput, rubric) {
24862
- return {
24863
- type: "points",
24864
- earned: 0,
24865
- total: 0,
24866
- message: null
24867
- };
24859
+ return noopValidator();
24868
24860
  }
24869
24861
  render() {
24870
24862
  return /*#__PURE__*/React.createElement(DefinitionConsumer, null, ({
@@ -24966,6 +24958,23 @@ var DeprecatedStandin$1 = {
24966
24958
  hidden: true
24967
24959
  };
24968
24960
 
24961
+ function dropdownValidator(userInput, rubric) {
24962
+ const selected = userInput.value;
24963
+ if (selected === 0) {
24964
+ return {
24965
+ type: "invalid",
24966
+ message: null
24967
+ };
24968
+ }
24969
+ const correct = rubric.choices[selected - 1].correct;
24970
+ return {
24971
+ type: "points",
24972
+ earned: correct ? 1 : 0,
24973
+ total: 1,
24974
+ message: null
24975
+ };
24976
+ }
24977
+
24969
24978
  class Dropdown extends React.Component {
24970
24979
  constructor(...args) {
24971
24980
  super(...args);
@@ -24992,24 +25001,11 @@ class Dropdown extends React.Component {
24992
25001
  };
24993
25002
  };
24994
25003
  this.simpleValidate = rubric => {
24995
- return Dropdown.validate(this.getUserInput(), rubric);
25004
+ return dropdownValidator(this.getUserInput(), rubric);
24996
25005
  };
24997
25006
  }
24998
25007
  static validate(userInput, rubric) {
24999
- const selected = userInput.value;
25000
- if (selected === 0) {
25001
- return {
25002
- type: "invalid",
25003
- message: null
25004
- };
25005
- }
25006
- const correct = rubric.choices[selected - 1].correct;
25007
- return {
25008
- type: "points",
25009
- earned: correct ? 1 : 0,
25010
- total: 1,
25011
- message: null
25012
- };
25008
+ return dropdownValidator(userInput, rubric);
25013
25009
  }
25014
25010
  render() {
25015
25011
  const children = [/*#__PURE__*/React.createElement(OptionItem, {
@@ -25092,12 +25088,7 @@ class Explanation extends React.Component {
25092
25088
  };
25093
25089
  }
25094
25090
  static validate(userInput, rubric) {
25095
- return {
25096
- type: "points",
25097
- earned: 0,
25098
- total: 0,
25099
- message: null
25100
- };
25091
+ return noopValidator();
25101
25092
  }
25102
25093
  componentDidMount() {
25103
25094
  this._mounted = true;
@@ -26925,6 +26916,7 @@ const staticTransform$4 = editorProps => {
26925
26916
  var Grapher$1 = {
26926
26917
  name: "grapher",
26927
26918
  displayName: "Grapher",
26919
+ hidden: true,
26928
26920
  widget: Grapher,
26929
26921
  transform: propTransform$2,
26930
26922
  staticTransform: staticTransform$4
@@ -27074,21 +27066,25 @@ var Group$1 = {
27074
27066
  displayName: "Group (SAT only)",
27075
27067
  widget: Group,
27076
27068
  traverseChildWidgets: traverseChildWidgets,
27077
- hidden: false,
27069
+ hidden: true,
27078
27070
  isLintable: true
27079
27071
  };
27080
27072
 
27073
+ /**
27074
+ * This is an iframe widget. It is used for rendering an iframe that
27075
+ * then communicates its state via window.postMessage
27076
+ * This is useful for embedding arbitrary visualizations/simulations with
27077
+ * completed conditions, such as the mazes and games in Algorithms.
27078
+ * It's particularly well suited for embedding our ProcessingJS programs,
27079
+ * but could also be used for embedding viz's hosted elsewhere.
27080
+ */
27081
27081
  const {
27082
27082
  updateQueryString
27083
27083
  } = Util;
27084
-
27085
27084
  /* This renders the iframe and handles validation via window.postMessage */
27086
27085
  class Iframe extends React.Component {
27087
27086
  constructor(..._args) {
27088
27087
  super(..._args);
27089
- this.getUserInput = () => {
27090
- return WidgetJsonifyDeprecated.getUserInput.call(this);
27091
- };
27092
27088
  this.handleMessageEvent = e => {
27093
27089
  // We receive data from the iframe that contains {passed: true/false}
27094
27090
  // and use that to set the status
@@ -27123,6 +27119,12 @@ class Iframe extends React.Component {
27123
27119
  componentWillUnmount() {
27124
27120
  $(window).off("message", this.handleMessageEvent);
27125
27121
  }
27122
+ getUserInput() {
27123
+ return {
27124
+ status: this.props.status,
27125
+ message: this.props.message
27126
+ };
27127
+ }
27126
27128
  render() {
27127
27129
  const style = {
27128
27130
  width: String(this.props.width),
@@ -27188,16 +27190,6 @@ class Iframe extends React.Component {
27188
27190
  /**
27189
27191
  * This is the widget's grading function
27190
27192
  */
27191
- Iframe.propTypes = _extends({}, propTypes, {
27192
- width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
27193
- height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
27194
- url: PropTypes.string,
27195
- settings: PropTypes.array,
27196
- status: PropTypes.oneOf(["incomplete", "incorrect", "correct"]),
27197
- message: PropTypes.string,
27198
- allowFullScreen: PropTypes.bool,
27199
- allowTopNavigation: PropTypes.bool
27200
- });
27201
27193
  Iframe.defaultProps = {
27202
27194
  status: "incomplete",
27203
27195
  // optional message
@@ -27266,12 +27258,7 @@ class ImageWidget extends React.Component {
27266
27258
  this.focus = () => {};
27267
27259
  }
27268
27260
  static validate(userInput, rubric) {
27269
- return {
27270
- type: "points",
27271
- earned: 0,
27272
- total: 0,
27273
- message: null
27274
- };
27261
+ return noopValidator();
27275
27262
  }
27276
27263
  // no-op
27277
27264
 
@@ -27592,12 +27579,7 @@ class Interaction extends React.Component {
27592
27579
  };
27593
27580
  }
27594
27581
  static validate(state, rubric) {
27595
- return {
27596
- type: "points",
27597
- earned: 0,
27598
- total: 0,
27599
- message: null
27600
- };
27582
+ return noopValidator();
27601
27583
  }
27602
27584
  UNSAFE_componentWillReceiveProps(nextProps) {
27603
27585
  // HACK(michaelpolyak): This widget is not wired to use the Perseus
@@ -29663,17 +29645,23 @@ function LockedLabel(props) {
29663
29645
 
29664
29646
  function GraphLockedLabelsLayer(props) {
29665
29647
  const {
29648
+ flags,
29666
29649
  lockedFigures
29667
29650
  } = props;
29668
29651
  return lockedFigures.map((figure, i) => {
29652
+ var _flags$mafs;
29669
29653
  if (figure.type === "label") {
29670
29654
  return /*#__PURE__*/React.createElement(LockedLabel, _extends({
29671
29655
  key: `label-${i}`
29672
29656
  }, figure));
29673
29657
  }
29674
-
29675
- // TODO(LEMS-2271): Add support for labels within
29676
- // other locked figure types
29658
+ if (flags != null && (_flags$mafs = flags["mafs"]) != null && _flags$mafs["locked-point-labels"] && figure.type === "point") {
29659
+ return /*#__PURE__*/React.createElement(React.Fragment, {
29660
+ key: `point-${i}`
29661
+ }, figure.labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
29662
+ key: `locked-figure-${i}-label-${j}`
29663
+ }, label))));
29664
+ }
29677
29665
  return null;
29678
29666
  });
29679
29667
  }
@@ -31884,6 +31872,7 @@ function useDraggablePx(args) {
31884
31872
  */
31885
31873
  const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
31886
31874
  const MafsGraph = props => {
31875
+ var _props$flags;
31887
31876
  const {
31888
31877
  state,
31889
31878
  dispatch,
@@ -31991,7 +31980,8 @@ const MafsGraph = props => {
31991
31980
  props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
31992
31981
  lockedFigures: props.lockedFigures,
31993
31982
  range: state.range
31994
- }))), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
31983
+ }))), ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["interactive-graph-locked-features-labels"]) && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
31984
+ flags: props.flags,
31995
31985
  lockedFigures: props.lockedFigures
31996
31986
  }), /*#__PURE__*/React.createElement(View, {
31997
31987
  style: {
@@ -34600,7 +34590,7 @@ class InteractiveGraph extends React.Component {
34600
34590
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
34601
34591
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
34602
34592
  return /*#__PURE__*/React.createElement(StatefulMafsGraph, _extends({}, this.props, {
34603
- showLabelsFlag: (_this$props$apiOption2 = this.props.apiOptions) == null || (_this$props$apiOption2 = _this$props$apiOption2.flags) == null || (_this$props$apiOption2 = _this$props$apiOption2["mafs"]) == null ? void 0 : _this$props$apiOption2["interactive-graph-locked-features-labels"],
34593
+ flags: (_this$props$apiOption2 = this.props.apiOptions) == null ? void 0 : _this$props$apiOption2.flags,
34604
34594
  ref: this.mafsRef,
34605
34595
  gridStep: gridStep,
34606
34596
  snapStep: snapStep,
@@ -35211,7 +35201,7 @@ function shouldUseMafs(mafsFlags, graph) {
35211
35201
  const staticTransform$3 = _.identity;
35212
35202
  var InteractiveGraph$1 = {
35213
35203
  name: "interactive-graph",
35214
- displayName: "Interactive graph",
35204
+ displayName: "Interactive graph (Assessments only)",
35215
35205
  widget: InteractiveGraph,
35216
35206
  staticTransform: staticTransform$3
35217
35207
  };
@@ -37147,11 +37137,6 @@ var LabelImage$1 = {
37147
37137
  isLintable: true
37148
37138
  };
37149
37139
 
37150
- let Layout = /*#__PURE__*/function (Layout) {
37151
- Layout["HORIZONTAL"] = "horizontal";
37152
- Layout["VERTICAL"] = "vertical";
37153
- return Layout;
37154
- }({});
37155
37140
  var ItemState = /*#__PURE__*/function (ItemState) {
37156
37141
  ItemState["STATIC"] = "static";
37157
37142
  ItemState["DRAGGING"] = "dragging";
@@ -37178,7 +37163,7 @@ class Placeholder extends React.Component {
37178
37163
  const {
37179
37164
  layout
37180
37165
  } = this.props;
37181
- const className = css(styles$d.card, styles$d.placeholder, layout === Layout.HORIZONTAL && styles$d.horizontalCard);
37166
+ const className = css(styles$d.card, styles$d.placeholder, layout === "horizontal" && styles$d.horizontalCard);
37182
37167
  const style = {
37183
37168
  width: this.props.width,
37184
37169
  height: this.props.height
@@ -37382,7 +37367,7 @@ class Draggable extends React.Component {
37382
37367
  // We need to keep backwards compatbility with rules specified directly
37383
37368
  // in CSS. Hence the hacky tacking on of manual classNames.
37384
37369
  // See sortable.less for details.
37385
- let className = css(styles$d.card, styles$d.draggable, layout === Layout.HORIZONTAL && styles$d.horizontalCard, layout === Layout.VERTICAL && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37370
+ let className = css(styles$d.card, styles$d.draggable, layout === "horizontal" && styles$d.horizontalCard, layout === "vertical" && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37386
37371
  if (!includePadding) {
37387
37372
  className += " perseus-sortable-draggable-unpadded";
37388
37373
  }
@@ -37511,7 +37496,7 @@ class Sortable extends React.Component {
37511
37496
  if (constraints != null && constraints.width) {
37512
37497
  // Items must be at least as wide as the specified constraint
37513
37498
  syncWidth = _.max(widths.concat(constraints.width));
37514
- } else if (layout === Layout.VERTICAL) {
37499
+ } else if (layout === "vertical") {
37515
37500
  // Sync widths to get a clean column
37516
37501
  syncWidth = _.max(widths);
37517
37502
  }
@@ -37519,7 +37504,7 @@ class Sortable extends React.Component {
37519
37504
  if (constraints != null && constraints.height) {
37520
37505
  // Items must be at least as high as the specified constraint
37521
37506
  syncHeight = _.max(heights.concat(constraints.height));
37522
- } else if (layout === Layout.HORIZONTAL) {
37507
+ } else if (layout === "horizontal") {
37523
37508
  // Sync widths to get a clean row
37524
37509
  syncHeight = _.max(heights);
37525
37510
  }
@@ -37601,7 +37586,7 @@ class Sortable extends React.Component {
37601
37586
 
37602
37587
  // @ts-expect-error - TS2551 - Property 'splice' does not exist on type 'readonly SortableItem[]'. Did you mean 'slice'?
37603
37588
  items.splice(currentIndex, 1);
37604
- if (this.props.layout === Layout.HORIZONTAL) {
37589
+ if (this.props.layout === "horizontal") {
37605
37590
  // @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'. | TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
37606
37591
  const midWidth = $draggable.offset().left - $sortable.offset().left;
37607
37592
  let sumWidth = 0;
@@ -37716,19 +37701,19 @@ class Sortable extends React.Component {
37716
37701
  // We need to keep backwards compatbility with rules specified directly
37717
37702
  // in CSS. See sortable.less for details.
37718
37703
  const className = css(styles$d.sortable) + " perseus-sortable";
37719
- const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === Layout.VERTICAL;
37720
- const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === Layout.HORIZONTAL;
37704
+ const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === "vertical";
37705
+ const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === "horizontal";
37721
37706
  _.each(this.state.items, function (item, i, items) {
37722
37707
  const isLast = i === items.length - 1;
37723
37708
  const isStatic = item.state === ItemState.STATIC || item.state === ItemState.DISABLED;
37724
37709
  let margin;
37725
37710
 
37726
37711
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37727
- if (this.props.layout === Layout.HORIZONTAL) {
37712
+ if (this.props.layout === "horizontal") {
37728
37713
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37729
37714
  margin = "0 " + this.props.margin + "px 0 0"; // right
37730
37715
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37731
- } else if (this.props.layout === Layout.VERTICAL) {
37716
+ } else if (this.props.layout === "vertical") {
37732
37717
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37733
37718
  margin = "0 0 " + this.props.margin + "px 0"; // bottom
37734
37719
  }
@@ -37802,7 +37787,7 @@ class Sortable extends React.Component {
37802
37787
  }
37803
37788
  }
37804
37789
  Sortable.defaultProps = {
37805
- layout: Layout.HORIZONTAL,
37790
+ layout: "horizontal",
37806
37791
  padding: true,
37807
37792
  disabled: false,
37808
37793
  constraints: {},
@@ -37861,7 +37846,6 @@ const styles$d = StyleSheet.create({
37861
37846
  }
37862
37847
  });
37863
37848
 
37864
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
37865
37849
  const {
37866
37850
  shuffle: shuffle$1,
37867
37851
  seededRNG
@@ -37995,7 +37979,7 @@ class Matcher extends React.Component {
37995
37979
  className: css(styles$c.column)
37996
37980
  }, /*#__PURE__*/React.createElement(Sortable, {
37997
37981
  options: left,
37998
- layout: Layout.VERTICAL,
37982
+ layout: "vertical",
37999
37983
  padding: this.props.padding,
38000
37984
  disabled: !this.props.orderMatters,
38001
37985
  constraints: constraints,
@@ -38008,11 +37992,9 @@ class Matcher extends React.Component {
38008
37992
  ref: "left"
38009
37993
  })), /*#__PURE__*/React.createElement("td", {
38010
37994
  className: css(styles$c.column, styles$c.columnRight)
38011
- }, /*#__PURE__*/React.createElement(Sortable
38012
- // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
38013
- , {
37995
+ }, /*#__PURE__*/React.createElement(Sortable, {
38014
37996
  options: right,
38015
- layout: Layout.VERTICAL,
37997
+ layout: "vertical",
38016
37998
  padding: this.props.padding,
38017
37999
  constraints: constraints,
38018
38000
  onMeasure: this.onMeasureRight,
@@ -38026,18 +38008,6 @@ class Matcher extends React.Component {
38026
38008
  }
38027
38009
  }
38028
38010
  Matcher.contextType = PerseusI18nContext;
38029
- Matcher.propTypes = {
38030
- apiOptions: ApiOptions.propTypes,
38031
- labels: PropTypes.array,
38032
- left: PropTypes.array,
38033
- onChange: PropTypes.func,
38034
- orderMatters: PropTypes.bool,
38035
- padding: PropTypes.bool,
38036
- problemNum: PropTypes.number,
38037
- right: PropTypes.array,
38038
- trackInteraction: PropTypes.func.isRequired,
38039
- linterContext: linterContextProps
38040
- };
38041
38011
  Matcher.defaultProps = {
38042
38012
  left: [],
38043
38013
  right: [],
@@ -38557,6 +38527,7 @@ const staticTransform$2 = editorProps => {
38557
38527
  var Matrix$1 = {
38558
38528
  name: "matrix",
38559
38529
  displayName: "Matrix",
38530
+ hidden: true,
38560
38531
  widget: Matrix,
38561
38532
  transform: propTransform$1,
38562
38533
  staticTransform: staticTransform$2,
@@ -38713,6 +38684,7 @@ const propUpgrades = {
38713
38684
  var Measurer$1 = {
38714
38685
  name: "measurer",
38715
38686
  displayName: "Measurer",
38687
+ hidden: true,
38716
38688
  widget: Measurer,
38717
38689
  version: {
38718
38690
  major: 1,
@@ -39602,12 +39574,7 @@ class MoleculeWidget extends React.Component {
39602
39574
  // TODO(colin): this is here as part of the interface for a component.
39603
39575
  // Figure out if there is something more appropriate that this should
39604
39576
  // return.
39605
- return {
39606
- type: "points",
39607
- earned: 0,
39608
- total: 0,
39609
- message: null
39610
- };
39577
+ return noopValidator();
39611
39578
  };
39612
39579
  }
39613
39580
  render() {
@@ -40214,15 +40181,11 @@ class PlaceholderCard extends React.Component {
40214
40181
  }, /*#__PURE__*/React.createElement("div", {
40215
40182
  className: "card placeholder",
40216
40183
  style: {
40217
- height: this.props.height
40184
+ height: this.props.width
40218
40185
  }
40219
40186
  }));
40220
40187
  }
40221
40188
  }
40222
- PlaceholderCard.propTypes = {
40223
- width: PropTypes.number.isRequired,
40224
- height: PropTypes.number.isRequired
40225
- };
40226
40189
  class DragHintCard extends React.Component {
40227
40190
  render() {
40228
40191
  return /*#__PURE__*/React.createElement("div", {
@@ -40232,10 +40195,6 @@ class DragHintCard extends React.Component {
40232
40195
  }));
40233
40196
  }
40234
40197
  }
40235
- const PropTypePosition = PropTypes.shape({
40236
- left: PropTypes.number,
40237
- top: PropTypes.number
40238
- });
40239
40198
  class Card extends React.Component {
40240
40199
  constructor(...args) {
40241
40200
  super(...args);
@@ -40316,7 +40275,7 @@ class Card extends React.Component {
40316
40275
  this.props.fakeRef !== nextProps.fakeRef;
40317
40276
  }
40318
40277
  componentDidUpdate(prevProps, prevState) {
40319
- if (this.props.animating && !prevProps.animating) {
40278
+ if (this.props.animating && !prevProps.animating && this.props.animateTo && this.props.startOffset) {
40320
40279
  // If we just were changed into animating, start the animation.
40321
40280
  // We pick the animation speed based on the distance that the card
40322
40281
  // needs to travel. (Why sqrt? Just because it looks nice -- with a
@@ -40339,10 +40298,11 @@ class Card extends React.Component {
40339
40298
  render() {
40340
40299
  let style = {};
40341
40300
  if (this.props.floating) {
40301
+ var _this$props$startOffs, _this$props$startOffs2;
40342
40302
  style = {
40343
40303
  position: "absolute",
40344
- left: this.props.startOffset.left,
40345
- top: this.props.startOffset.top
40304
+ left: (_this$props$startOffs = this.props.startOffset) == null ? void 0 : _this$props$startOffs.left,
40305
+ top: (_this$props$startOffs2 = this.props.startOffset) == null ? void 0 : _this$props$startOffs2.top
40346
40306
  };
40347
40307
  }
40348
40308
  if (this.props.width) {
@@ -40352,7 +40312,7 @@ class Card extends React.Component {
40352
40312
  if (this.props.stack) {
40353
40313
  className.push("stack");
40354
40314
  }
40355
- if (this.props.floating && !this.props.animating) {
40315
+ if (this.props.floating && !this.props.animating && this.props.mouse && this.props.startMouse) {
40356
40316
  className.push("dragging");
40357
40317
  style.left += this.props.mouse.left - this.props.startMouse.left;
40358
40318
  style.top += this.props.mouse.top - this.props.startMouse.top;
@@ -40377,28 +40337,13 @@ class Card extends React.Component {
40377
40337
  }
40378
40338
  }
40379
40339
  Card.contextType = PerseusI18nContext;
40380
- Card.propTypes = {
40381
- floating: PropTypes.bool.isRequired,
40382
- animating: PropTypes.bool,
40383
- width: PropTypes.number,
40384
- stack: PropTypes.bool,
40385
- onMouseDown: PropTypes.func,
40386
- onMouseMove: PropTypes.func,
40387
- onMouseUp: PropTypes.func,
40388
- // Used only for floating/animating cards
40389
- startMouse: PropTypePosition,
40390
- startOffset: PropTypePosition,
40391
- animateTo: PropTypePosition,
40392
- onAnimationEnd: PropTypes.func,
40393
- linterContext: linterContextProps
40394
- };
40395
40340
  Card.defaultProps = {
40396
40341
  stack: false,
40397
40342
  animating: false,
40398
40343
  linterContext: linterContextDefault
40399
40344
  };
40400
40345
  const NORMAL = "normal",
40401
- HORIZONTAL$1 = "horizontal";
40346
+ HORIZONTAL = "horizontal";
40402
40347
  class Orderer extends React.Component {
40403
40348
  constructor(...args) {
40404
40349
  super(...args);
@@ -40545,7 +40490,7 @@ class Orderer extends React.Component {
40545
40490
  };
40546
40491
  this.findCorrectIndex = (draggable, list) => {
40547
40492
  // Find the correct index for a card given the current cards.
40548
- const isHorizontal = this.props.layout === HORIZONTAL$1;
40493
+ const isHorizontal = this.props.layout === HORIZONTAL;
40549
40494
  // eslint-disable-next-line react/no-string-refs
40550
40495
  // @ts-expect-error - TS2769 - No overload matches this call.
40551
40496
  const $dragList = $(ReactDOM__default.findDOMNode(this.refs.dragList));
@@ -40595,7 +40540,7 @@ class Orderer extends React.Component {
40595
40540
  if (draggable == null) {
40596
40541
  return false;
40597
40542
  }
40598
- const isHorizontal = this.props.layout === HORIZONTAL$1,
40543
+ const isHorizontal = this.props.layout === HORIZONTAL,
40599
40544
  // @ts-expect-error - TS2769 - No overload matches this call.
40600
40545
  $draggable = $(ReactDOM__default.findDOMNode(draggable)),
40601
40546
  // eslint-disable-next-line react/no-string-refs
@@ -40767,7 +40712,7 @@ Orderer.defaultProps = {
40767
40712
  options: [],
40768
40713
  correctOptions: [],
40769
40714
  height: NORMAL,
40770
- layout: HORIZONTAL$1,
40715
+ layout: HORIZONTAL,
40771
40716
  linterContext: linterContextDefault
40772
40717
  };
40773
40718
  _.extend(Orderer, {
@@ -40790,6 +40735,7 @@ _.extend(Orderer, {
40790
40735
  var Orderer$1 = {
40791
40736
  name: "orderer",
40792
40737
  displayName: "Orderer",
40738
+ hidden: true,
40793
40739
  widget: Orderer,
40794
40740
  isLintable: true
40795
40741
  };
@@ -42594,12 +42540,7 @@ class Passage extends React.Component {
42594
42540
  };
42595
42541
  }
42596
42542
  static validate(state, rubric) {
42597
- return {
42598
- type: "points",
42599
- earned: 0,
42600
- total: 0,
42601
- message: null
42602
- };
42543
+ return noopValidator();
42603
42544
  }
42604
42545
  componentDidMount() {
42605
42546
  this._updateState();
@@ -42965,6 +42906,7 @@ Passage.defaultProps = {
42965
42906
  var Passage$1 = {
42966
42907
  name: "passage",
42967
42908
  displayName: "Passage (SAT only)",
42909
+ hidden: true,
42968
42910
  widget: Passage,
42969
42911
  transform: editorProps => {
42970
42912
  return _.pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
@@ -42976,23 +42918,21 @@ var Passage$1 = {
42976
42918
  class PassageRefTarget extends React.Component {
42977
42919
  constructor(..._args) {
42978
42920
  super(..._args);
42921
+ // TODO passage-ref-target isn't interactive; remove
42979
42922
  this.getUserInput = () => {
42980
- return WidgetJsonifyDeprecated.getUserInput.call(this);
42923
+ return;
42981
42924
  };
42925
+ // TODO passage-ref-target isn't interactive; remove
42982
42926
  this.change = (...args) => {
42983
42927
  return change.apply(this, args);
42984
42928
  };
42929
+ // TODO passage-ref-target isn't interactive; remove
42985
42930
  this.simpleValidate = rubric => {
42986
42931
  return PassageRefTarget.validate(this.getUserInput(), rubric);
42987
42932
  };
42988
42933
  }
42989
42934
  static validate(state, rubric) {
42990
- return {
42991
- type: "points",
42992
- earned: 0,
42993
- total: 0,
42994
- message: null
42995
- };
42935
+ return noopValidator();
42996
42936
  }
42997
42937
  render() {
42998
42938
  return /*#__PURE__*/React.createElement(Renderer, {
@@ -44054,6 +43994,7 @@ const staticTransform = _.identity;
44054
43994
  var Plotter$1 = {
44055
43995
  name: "plotter",
44056
43996
  displayName: "Plotter",
43997
+ hidden: true,
44057
43998
  widget: Plotter,
44058
43999
  staticTransform: staticTransform
44059
44000
  };
@@ -44104,17 +44045,13 @@ var PythonProgram$1 = {
44104
44045
  widget: PythonProgram
44105
44046
  };
44106
44047
 
44107
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
44108
44048
  const {
44109
44049
  shuffle
44110
44050
  } = Util;
44111
- const HORIZONTAL = "horizontal";
44112
- const VERTICAL = "vertical";
44113
44051
  class Sorter extends React.Component {
44114
44052
  constructor(...args) {
44115
44053
  super(...args);
44116
- // @ts-expect-error - TS2564 - Property '_isMounted' has no initializer and is not definitely assigned in the constructor.
44117
- this._isMounted = void 0;
44054
+ this._isMounted = false;
44118
44055
  this.state = {
44119
44056
  changed: false
44120
44057
  };
@@ -44188,9 +44125,7 @@ class Sorter extends React.Component {
44188
44125
  const marginPx = apiOptions.isMobile ? 8 : 5;
44189
44126
  return /*#__PURE__*/React.createElement("div", {
44190
44127
  className: "perseus-widget-sorter perseus-clearfix"
44191
- }, /*#__PURE__*/React.createElement(Sortable
44192
- // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
44193
- , {
44128
+ }, /*#__PURE__*/React.createElement(Sortable, {
44194
44129
  options: options,
44195
44130
  layout: this.props.layout,
44196
44131
  margin: marginPx,
@@ -44203,20 +44138,9 @@ class Sorter extends React.Component {
44203
44138
  }));
44204
44139
  }
44205
44140
  }
44206
- Sorter.propTypes = {
44207
- apiOptions: ApiOptions.propTypes,
44208
- // eslint-disable-next-line react/forbid-prop-types
44209
- correct: PropTypes.array,
44210
- layout: PropTypes.oneOf([HORIZONTAL, VERTICAL]),
44211
- onChange: PropTypes.func,
44212
- padding: PropTypes.bool,
44213
- problemNum: PropTypes.number,
44214
- trackInteraction: PropTypes.func.isRequired,
44215
- linterContext: linterContextProps
44216
- };
44217
44141
  Sorter.defaultProps = {
44218
44142
  correct: [],
44219
- layout: HORIZONTAL,
44143
+ layout: "horizontal",
44220
44144
  padding: true,
44221
44145
  problemNum: 0,
44222
44146
  onChange: function () {},
@@ -44232,7 +44156,6 @@ var Sorter$1 = {
44232
44156
  const {
44233
44157
  assert
44234
44158
  } = InteractiveUtil;
44235
-
44236
44159
  /* Input handling: Maps a (row, column) pair to a unique ref used by React,
44237
44160
  * and extracts (row, column) pairs from input paths, used to allow outsiders
44238
44161
  * to focus, blur, set input values, etc. */
@@ -44419,16 +44342,6 @@ class Table extends React.Component {
44419
44342
  }
44420
44343
  }
44421
44344
  Table.contextType = PerseusI18nContext;
44422
- Table.propTypes = {
44423
- answers: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
44424
- editableHeaders: PropTypes.bool,
44425
- // The editor to use when editableHeaders is enabled
44426
- Editor: PropTypes.func,
44427
- headers: PropTypes.arrayOf(PropTypes.string),
44428
- keypadElement: keypadElementPropType,
44429
- trackInteraction: PropTypes.func.isRequired,
44430
- linterContext: linterContextProps
44431
- };
44432
44345
  Table.defaultProps = function () {
44433
44346
  const defaultRows = 4;
44434
44347
  const defaultColumns = 1;
@@ -44630,12 +44543,7 @@ class Video extends React.Component {
44630
44543
  * of Khan Academy videos.
44631
44544
  */
44632
44545
  static validate(userInput, rubric) {
44633
- return {
44634
- type: "points",
44635
- earned: 0,
44636
- total: 0,
44637
- message: null
44638
- };
44546
+ return noopValidator();
44639
44547
  }
44640
44548
  render() {
44641
44549
  const {
@@ -44740,7 +44648,7 @@ const init = function init(options) {
44740
44648
 
44741
44649
  // This file is processed by a Rollup plugin (replace) to inject the production
44742
44650
  const libName = "@khanacademy/perseus";
44743
- const libVersion = "33.3.0";
44651
+ const libVersion = "34.0.0";
44744
44652
  addLibraryVersionToPerseusDebug(libName, libVersion);
44745
44653
 
44746
44654
  const apiVersion = {
@@ -48203,5 +48111,5 @@ const EditorJsonify = {
48203
48111
  }
48204
48112
  };
48205
48113
 
48206
- export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48114
+ export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48207
48115
  //# sourceMappingURL=index.js.map