@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/index.js CHANGED
@@ -4,7 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var KAS = require('@khanacademy/kas');
6
6
  var mathInput = require('@khanacademy/math-input');
7
- var perseusCore = require('@khanacademy/perseus-core');
8
7
  var PerseusLinter = require('@khanacademy/perseus-linter');
9
8
  var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
10
9
  var Tooltip$1 = require('@khanacademy/wonder-blocks-tooltip');
@@ -19,6 +18,7 @@ var wonderBlocksPopover = require('@khanacademy/wonder-blocks-popover');
19
18
  var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
20
19
  var $ = require('jquery');
21
20
  var uuid = require('uuid');
21
+ var perseusCore = require('@khanacademy/perseus-core');
22
22
  var PropTypes = require('prop-types');
23
23
  var kmath = require('@khanacademy/kmath');
24
24
  var wonderStuffCore = require('@khanacademy/wonder-stuff-core');
@@ -845,23 +845,6 @@ var dependencies = /*#__PURE__*/Object.freeze({
845
845
  useDependencies: useDependencies
846
846
  });
847
847
 
848
- // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
849
- // getDependencies() internally... simplify usage to just using a "singleton"
850
- // which get's the injected logger.
851
- /**
852
- * Provides logging infrastructure for Perseus
853
- */
854
- const Log = {
855
- // Logs a message
856
- log: (message, extra) => {
857
- getDependencies().Log.log(message, extra);
858
- },
859
- // Logs an error with a message
860
- error: (message, kind, extra) => {
861
- getDependencies().Log.error(message, kind, extra);
862
- }
863
- };
864
-
865
848
  /**
866
849
  * These are things that widgets should exclude when serializing themselves.
867
850
  *
@@ -1304,6 +1287,23 @@ const ClassNames = {
1304
1287
  MOBILE: "perseus-mobile"
1305
1288
  };
1306
1289
 
1290
+ // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
1291
+ // getDependencies() internally... simplify usage to just using a "singleton"
1292
+ // which get's the injected logger.
1293
+ /**
1294
+ * Provides logging infrastructure for Perseus
1295
+ */
1296
+ const Log = {
1297
+ // Logs a message
1298
+ log: (message, extra) => {
1299
+ getDependencies().Log.log(message, extra);
1300
+ },
1301
+ // Logs an error with a message
1302
+ error: (message, kind, extra) => {
1303
+ getDependencies().Log.error(message, kind, extra);
1304
+ }
1305
+ };
1306
+
1307
1307
  const KhanMath = {
1308
1308
  // Simplify formulas before display
1309
1309
  cleanMath: function (expr) {
@@ -2219,6 +2219,176 @@ const KhanAnswerTypes = {
2219
2219
  }
2220
2220
  };
2221
2221
 
2222
+ /**
2223
+ * Get the character used for separating decimals.
2224
+ */
2225
+ const getDecimalSeparator = locale => {
2226
+ switch (locale) {
2227
+ // TODO(somewhatabstract): Remove this when Chrome supports the `ka`
2228
+ // locale properly.
2229
+ // https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
2230
+ //
2231
+ // Supported locales in Chrome:
2232
+ // https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
2233
+ case "ka":
2234
+ return ",";
2235
+ default:
2236
+ const numberWithDecimalSeparator = 1.1;
2237
+ // TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
2238
+ // support Safari 12.
2239
+ const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
2240
+ // 0x661 is ARABIC-INDIC DIGIT ONE
2241
+ // 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
2242
+ .match(/[^\d\u0661\u06F1]/);
2243
+ return match?.[0] ?? ".";
2244
+ }
2245
+ };
2246
+
2247
+ /* Content creators input a list of answers which are matched from top to
2248
+ * bottom. The intent is that they can include spcific solutions which should
2249
+ * be graded as correct or incorrect (or ungraded!) first, then get more
2250
+ * general.
2251
+ *
2252
+ * We iterate through each answer, trying to match it with the user's input
2253
+ * using the following angorithm:
2254
+ * - Try to parse the user's input. If it doesn't parse then return "not
2255
+ * graded".
2256
+ * - For each answer:
2257
+ * ~ Try to validate the user's input against the answer. The answer is
2258
+ * expected to parse.
2259
+ * ~ If the user's input validates (the validator judges it "correct"), we've
2260
+ * matched and can stop considering answers.
2261
+ * - If there were no matches or the matching answer is considered "ungraded",
2262
+ * show the user an error. TODO(joel) - what error?
2263
+ * - Otherwise, pass through the resulting points and message.
2264
+ */
2265
+ function expressionValidator(userInput, rubric) {
2266
+ let onInputError = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};
2267
+ let strings = arguments.length > 3 ? arguments[3] : undefined;
2268
+ let locale = arguments.length > 4 ? arguments[4] : undefined;
2269
+ const options = ___default["default"].clone(rubric);
2270
+ ___default["default"].extend(options, {
2271
+ decimal_separator: getDecimalSeparator(locale)
2272
+ });
2273
+ const createValidator = answer => {
2274
+ // We give options to KAS.parse here because it is parsing the
2275
+ // solution answer, not the student answer, and we don't want a
2276
+ // solution to work if the student is using a different language
2277
+ // (different from the content creation language, ie. English).
2278
+ const expression = KAS__namespace.parse(answer.value, rubric);
2279
+ // An answer may not be parsed if the expression was defined
2280
+ // incorrectly. For example if the answer is using a symbol defined
2281
+ // in the function variables list for the expression.
2282
+ if (!expression.parsed) {
2283
+ /* c8 ignore next */
2284
+ Log.error("Unable to parse solution answer for expression", perseusCore.Errors.InvalidInput, {
2285
+ loggedMetadata: {
2286
+ rubric: JSON.stringify(rubric)
2287
+ }
2288
+ });
2289
+ return null;
2290
+ }
2291
+ return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, ___default["default"]({}).extend(options, {
2292
+ simplify: answer.simplify,
2293
+ form: answer.form
2294
+ }), strings);
2295
+ };
2296
+
2297
+ // Find the first answer form that matches the user's input and that
2298
+ // is considered correct. Also, track whether the input is
2299
+ // considered "empty" for all answer forms, and keep the validation
2300
+ // result for the first answer form for which the user's input was
2301
+ // considered "ungraded".
2302
+ // (Terminology reminder: the answer forms are provided by the
2303
+ // assessment items; they are not the user's input. Each one might
2304
+ // represent a correct answer, an incorrect one (if the exercise
2305
+ // creator has predicted certain common wrong answers and wants to
2306
+ // provide guidance via a message), or an ungraded one (same idea,
2307
+ // but without giving the user an incorrect mark for the question).
2308
+ let matchingAnswerForm;
2309
+ let matchMessage;
2310
+ let allEmpty = true;
2311
+ let firstUngradedResult;
2312
+ for (const answerForm of rubric.answerForms || []) {
2313
+ const validator = createValidator(answerForm);
2314
+ if (!validator) {
2315
+ continue;
2316
+ }
2317
+ const result = validator(userInput);
2318
+
2319
+ // Short-circuit as soon as the user's input matches some answer
2320
+ // (independently of whether the answer is correct)
2321
+ if (result.correct) {
2322
+ matchingAnswerForm = answerForm;
2323
+ matchMessage = result.message || "";
2324
+ break;
2325
+ }
2326
+ allEmpty = allEmpty && result.empty;
2327
+ // If this answer form is correct and the user's input is considered
2328
+ // "ungraded" for it, we'll want to keep the evaluation result for
2329
+ // later. If the user's input doesn't match any answer forms, we'll
2330
+ // show the message from this validation.
2331
+ if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
2332
+ firstUngradedResult = result;
2333
+ }
2334
+ }
2335
+
2336
+ // Now check to see if we matched any answer form at all, and if
2337
+ // we did, whether it's considered correct, incorrect, or ungraded
2338
+ if (!matchingAnswerForm) {
2339
+ if (firstUngradedResult) {
2340
+ // While we didn't directly match with any answer form, we
2341
+ // did at some point get an "ungraded" validation result,
2342
+ // which might indicate e.g. a mismatch in variable casing.
2343
+ // We'll return "invalid", which will let the user try again
2344
+ // with no penalty, and the hopefully helpful validation
2345
+ // message.
2346
+ return {
2347
+ type: "invalid",
2348
+ message: firstUngradedResult.message,
2349
+ suppressAlmostThere: firstUngradedResult.suppressAlmostThere
2350
+ };
2351
+ }
2352
+ if (allEmpty) {
2353
+ // If everything graded as empty, it's invalid.
2354
+ return {
2355
+ type: "invalid",
2356
+ message: null
2357
+ };
2358
+ }
2359
+ // We fell through all the possibilities and we're not empty,
2360
+ // so the answer is considered incorrect.
2361
+ return {
2362
+ type: "points",
2363
+ earned: 0,
2364
+ total: 1
2365
+ };
2366
+ }
2367
+ if (matchingAnswerForm.considered === "ungraded") {
2368
+ // We matched an ungraded answer form - return "invalid", which
2369
+ // will let the user try again with no penalty
2370
+ const apiResult = onInputError(null,
2371
+ // Reserved for some widget identifier
2372
+ userInput, matchMessage);
2373
+ return {
2374
+ type: "invalid",
2375
+ message: apiResult === false ? null : matchMessage
2376
+ };
2377
+ }
2378
+ // We matched a graded answer form, so we can now tell the user
2379
+ // whether their input was correct or incorrect, and hand out
2380
+ // points accordingly
2381
+ // TODO(eater): Seems silly to translate result to this
2382
+ // invalid/points thing and immediately translate it back in
2383
+ // ItemRenderer.scoreInput()
2384
+ return {
2385
+ type: "points",
2386
+ earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
2387
+ total: 1,
2388
+ message: matchMessage
2389
+ };
2390
+ }
2391
+
2222
2392
  // Map of international operator names to their English equivalents
2223
2393
  const englishOperators = {
2224
2394
  arctg: "arctan",
@@ -2245,181 +2415,18 @@ const deriveKeypadVersion = apiOptions => {
2245
2415
  // can remove this `virtualKeypadVersion` field entirely.
2246
2416
  return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : "MATH_INPUT_KEYPAD_V2";
2247
2417
  };
2248
- /**
2249
- * Get the character used for separating decimals.
2250
- */
2251
- const getDecimalSeparator = locale => {
2252
- switch (locale) {
2253
- // TODO(somewhatabstract): Remove this when Chrome supports the `ka`
2254
- // locale properly.
2255
- // https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
2256
- //
2257
- // Supported locales in Chrome:
2258
- // https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
2259
- case "ka":
2260
- return ",";
2261
- default:
2262
- const numberWithDecimalSeparator = 1.1;
2263
- // TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
2264
- // support Safari 12.
2265
- const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
2266
- // 0x661 is ARABIC-INDIC DIGIT ONE
2267
- // 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
2268
- .match(/[^\d\u0661\u06F1]/);
2269
- return match?.[0] ?? ".";
2270
- }
2271
- };
2272
-
2273
2418
  // The new, MathQuill input expression widget
2274
2419
  class Expression extends React__namespace.Component {
2275
2420
  static contextType = PerseusI18nContext;
2276
2421
  _textareaId = `expression_textarea_${Date.now()}`;
2277
2422
  _isMounted = false;
2278
2423
 
2279
- //#region Previously a class extension
2280
- /* Content creators input a list of answers which are matched from top to
2281
- * bottom. The intent is that they can include spcific solutions which should
2282
- * be graded as correct or incorrect (or ungraded!) first, then get more
2283
- * general.
2284
- *
2285
- * We iterate through each answer, trying to match it with the user's input
2286
- * using the following angorithm:
2287
- * - Try to parse the user's input. If it doesn't parse then return "not
2288
- * graded".
2289
- * - For each answer:
2290
- * ~ Try to validate the user's input against the answer. The answer is
2291
- * expected to parse.
2292
- * ~ If the user's input validates (the validator judges it "correct"), we've
2293
- * matched and can stop considering answers.
2294
- * - If there were no matches or the matching answer is considered "ungraded",
2295
- * show the user an error. TODO(joel) - what error?
2296
- * - Otherwise, pass through the resulting points and message.
2297
- */
2424
+ // TODO remove this in favor of just using expressionValidator
2298
2425
  static validate(userInput, rubric) {
2299
2426
  let onInputError = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};
2300
2427
  let strings = arguments.length > 3 ? arguments[3] : undefined;
2301
2428
  let locale = arguments.length > 4 ? arguments[4] : undefined;
2302
- const options = ___default["default"].clone(rubric);
2303
- ___default["default"].extend(options, {
2304
- decimal_separator: getDecimalSeparator(locale)
2305
- });
2306
- const createValidator = answer => {
2307
- // We give options to KAS.parse here because it is parsing the
2308
- // solution answer, not the student answer, and we don't want a
2309
- // solution to work if the student is using a different language
2310
- // (different from the content creation language, ie. English).
2311
- const expression = KAS__namespace.parse(answer.value, rubric);
2312
- // An answer may not be parsed if the expression was defined
2313
- // incorrectly. For example if the answer is using a symbol defined
2314
- // in the function variables list for the expression.
2315
- if (!expression.parsed) {
2316
- /* c8 ignore next */
2317
- Log.error("Unable to parse solution answer for expression", perseusCore.Errors.InvalidInput, {
2318
- loggedMetadata: {
2319
- rubric: JSON.stringify(rubric)
2320
- }
2321
- });
2322
- return null;
2323
- }
2324
- return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, ___default["default"]({}).extend(options, {
2325
- simplify: answer.simplify,
2326
- form: answer.form
2327
- }), strings);
2328
- };
2329
-
2330
- // Find the first answer form that matches the user's input and that
2331
- // is considered correct. Also, track whether the input is
2332
- // considered "empty" for all answer forms, and keep the validation
2333
- // result for the first answer form for which the user's input was
2334
- // considered "ungraded".
2335
- // (Terminology reminder: the answer forms are provided by the
2336
- // assessment items; they are not the user's input. Each one might
2337
- // represent a correct answer, an incorrect one (if the exercise
2338
- // creator has predicted certain common wrong answers and wants to
2339
- // provide guidance via a message), or an ungraded one (same idea,
2340
- // but without giving the user an incorrect mark for the question).
2341
- let matchingAnswerForm;
2342
- let matchMessage;
2343
- let allEmpty = true;
2344
- let firstUngradedResult;
2345
- for (const answerForm of rubric.answerForms || []) {
2346
- const validate = createValidator(answerForm);
2347
- if (!validate) {
2348
- continue;
2349
- }
2350
- const result = validate(userInput);
2351
-
2352
- // Short-circuit as soon as the user's input matches some answer
2353
- // (independently of whether the answer is correct)
2354
- if (result.correct) {
2355
- matchingAnswerForm = answerForm;
2356
- matchMessage = result.message || "";
2357
- break;
2358
- }
2359
- allEmpty = allEmpty && result.empty;
2360
- // If this answer form is correct and the user's input is considered
2361
- // "ungraded" for it, we'll want to keep the evaluation result for
2362
- // later. If the user's input doesn't match any answer forms, we'll
2363
- // show the message from this validation.
2364
- if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
2365
- firstUngradedResult = result;
2366
- }
2367
- }
2368
-
2369
- // Now check to see if we matched any answer form at all, and if
2370
- // we did, whether it's considered correct, incorrect, or ungraded
2371
- if (!matchingAnswerForm) {
2372
- if (firstUngradedResult) {
2373
- // While we didn't directly match with any answer form, we
2374
- // did at some point get an "ungraded" validation result,
2375
- // which might indicate e.g. a mismatch in variable casing.
2376
- // We'll return "invalid", which will let the user try again
2377
- // with no penalty, and the hopefully helpful validation
2378
- // message.
2379
- return {
2380
- type: "invalid",
2381
- message: firstUngradedResult.message,
2382
- suppressAlmostThere: firstUngradedResult.suppressAlmostThere
2383
- };
2384
- }
2385
- if (allEmpty) {
2386
- // If everything graded as empty, it's invalid.
2387
- return {
2388
- type: "invalid",
2389
- message: null
2390
- };
2391
- }
2392
- // We fell through all the possibilities and we're not empty,
2393
- // so the answer is considered incorrect.
2394
- return {
2395
- type: "points",
2396
- earned: 0,
2397
- total: 1
2398
- };
2399
- }
2400
- if (matchingAnswerForm.considered === "ungraded") {
2401
- // We matched an ungraded answer form - return "invalid", which
2402
- // will let the user try again with no penalty
2403
- const apiResult = onInputError(null,
2404
- // Reserved for some widget identifier
2405
- userInput, matchMessage);
2406
- return {
2407
- type: "invalid",
2408
- message: apiResult === false ? null : matchMessage
2409
- };
2410
- }
2411
- // We matched a graded answer form, so we can now tell the user
2412
- // whether their input was correct or incorrect, and hand out
2413
- // points accordingly
2414
- // TODO(eater): Seems silly to translate result to this
2415
- // invalid/points thing and immediately translate it back in
2416
- // ItemRenderer.scoreInput()
2417
- return {
2418
- type: "points",
2419
- earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
2420
- total: 1,
2421
- message: matchMessage
2422
- };
2429
+ return expressionValidator(userInput, rubric, onInputError, strings, locale);
2423
2430
  }
2424
2431
  static getUserInputFromProps(props) {
2425
2432
  return normalizeTex(props.value);
@@ -2506,7 +2513,7 @@ class Expression extends React__namespace.Component {
2506
2513
  scoring,
2507
2514
  ...rubric
2508
2515
  } = _ref;
2509
- const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
2516
+ const score = expressionValidator(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
2510
2517
 
2511
2518
  // "scoring" is a flag that indicates when we are checking answers.
2512
2519
  // otherwise, we may just be checking validity after changes.
@@ -2780,10 +2787,10 @@ const ExpressionWithDependencies = /*#__PURE__*/React__namespace.forwardRef((pro
2780
2787
 
2781
2788
  // HACK: Propogate "static" methods onto our wrapper component.
2782
2789
  // In the future we should adjust client apps to not depend on these static
2783
- // methods and instead adjust Peresus to provide these facilities through
2790
+ // methods and instead adjust Perseus to provide these facilities through
2784
2791
  // instance methods on our Renderers.
2785
2792
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2786
- ExpressionWithDependencies.validate = Expression.validate;
2793
+ ExpressionWithDependencies.validate = expressionValidator;
2787
2794
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2788
2795
  ExpressionWithDependencies.getUserInputFromProps = Expression.getUserInputFromProps;
2789
2796
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
@@ -19812,9 +19819,7 @@ const isIdPathPrefix = function (prefixArray, wholeArray) {
19812
19819
 
19813
19820
  // The return type for getUserInput. Widgets have full control of what is
19814
19821
  // returned so it's not easily typed (some widgets return a scalar (string),
19815
- // some return a custom-built object, some delegate to
19816
- // `WidgetJsonifyDeprecated` which returns an object containing widget props
19817
- // (filtered by deprecated keys)).
19822
+ // some return a custom-built object
19818
19823
  /**
19819
19824
  * The Widget type represents the common API that the Renderer uses to interact
19820
19825
  * with all widgets. All widgets must implement the methods in this API, unless
@@ -22254,6 +22259,130 @@ const answerFormButtons = [{
22254
22259
  value: "pi",
22255
22260
  content: "\u03C0"
22256
22261
  }];
22262
+
22263
+ // This function checks if the user inputted a percent value, parsing
22264
+ // it as a number (and maybe scaling) so that it can be graded.
22265
+ // NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
22266
+ // can accept several input forms with or without "%", the decision
22267
+ // to parse based on the presence of "%" in the input, is so that we
22268
+ // don't accidently scale the user typed value before grading, CP-930.
22269
+ function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
22270
+ // If the input value is not a string ending with "%", then there's
22271
+ // nothing more to do. The value will be graded as inputted by user.
22272
+ if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
22273
+ return inputValue;
22274
+ }
22275
+ const value = parseFloat(inputValue.slice(0, -1));
22276
+ // If the input value stripped of the "%" cannot be parsed as a
22277
+ // number (the slice is not really necessary for parseFloat to work
22278
+ // if the string starts with a number) then return the original
22279
+ // input for grading.
22280
+ if (isNaN(value)) {
22281
+ return inputValue;
22282
+ }
22283
+
22284
+ // Next, if all correct answers are in the range of |0,1| then we
22285
+ // scale the user typed value. We assume this is the correct thing
22286
+ // to do since the input value ends with "%".
22287
+ if (normalizedAnswerExpected) {
22288
+ return value / 100;
22289
+ }
22290
+
22291
+ // Otherwise, we return input valuåe (number) stripped of the "%".
22292
+ return value;
22293
+ }
22294
+ function numericInputValidator(userInput, rubric, strings) {
22295
+ const defaultAnswerForms = answerFormButtons.map(e => e["value"])
22296
+ // Don't default to validating the answer as a pi answer
22297
+ // if answerForm isn't set on the answer
22298
+ // https://khanacademy.atlassian.net/browse/LC-691
22299
+ .filter(e => e !== "pi");
22300
+ const createValidator = answer => {
22301
+ const stringAnswer = `${answer.value}`;
22302
+
22303
+ // Always validate against the provided answer forms (pi, decimal, etc.)
22304
+ const validatorForms = [...(answer.answerForms ?? [])];
22305
+
22306
+ // When an answer is set to strict, we validate using ONLY
22307
+ // the provided answerForms. If strict is false, or if there
22308
+ // were no provided answer forms, we will include all
22309
+ // of the default answer forms in our validator.
22310
+ if (!answer.strict || validatorForms.length === 0) {
22311
+ validatorForms.push(...defaultAnswerForms);
22312
+ }
22313
+ return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
22314
+ message: answer.message,
22315
+ simplify: answer.status === "correct" ? answer.simplify : "optional",
22316
+ inexact: true,
22317
+ // TODO(merlob) backfill / delete
22318
+ maxError: answer.maxError,
22319
+ forms: validatorForms
22320
+ }, strings);
22321
+ };
22322
+
22323
+ // We may have received TeX; try to parse it before grading.
22324
+ // If `currentValue` is not TeX, this should be a no-op.
22325
+ const currentValue = ParseTex(userInput.currentValue);
22326
+ const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22327
+ const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
22328
+
22329
+ // Look through all correct answers for one that matches either
22330
+ // precisely or approximately and return the appropriate message:
22331
+ // - if precise, return the message that the answer came with
22332
+ // - if it needs to be simplified, etc., show that message
22333
+ let result = correctAnswers.map(answer => {
22334
+ // The coefficient is an attribute of the widget
22335
+ let localValue = currentValue;
22336
+ if (rubric.coefficient) {
22337
+ if (!localValue) {
22338
+ localValue = 1;
22339
+ } else if (localValue === "-") {
22340
+ localValue = -1;
22341
+ }
22342
+ }
22343
+ const validate = createValidator(answer);
22344
+ return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
22345
+ }).find(match => match.correct || match.empty);
22346
+ if (!result) {
22347
+ // Otherwise, if the guess is not correct
22348
+ const otherAnswers = [].concat(
22349
+ // @ts-expect-error - TS2769 - No overload matches this call.
22350
+ rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
22351
+
22352
+ // Look through all other answers and if one matches either
22353
+ // precisely or approximately return the answer's message
22354
+ const match = otherAnswers.find(answer => {
22355
+ const validate = createValidator(answer);
22356
+ return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
22357
+ });
22358
+ result = {
22359
+ // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22360
+ empty: match ? match.status === "ungraded" : false,
22361
+ // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22362
+ correct: match ? match.status === "correct" : false,
22363
+ // @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
22364
+ message: match ? match.message : null,
22365
+ guess: currentValue
22366
+ };
22367
+ }
22368
+
22369
+ // TODO(eater): Seems silly to translate result to this
22370
+ // invalid/points thing and immediately translate it
22371
+ // back in ItemRenderer.scoreInput()
22372
+ if (result.empty) {
22373
+ return {
22374
+ type: "invalid",
22375
+ message: result.message
22376
+ };
22377
+ }
22378
+ return {
22379
+ type: "points",
22380
+ earned: result.correct ? 1 : 0,
22381
+ total: 1,
22382
+ message: result.message
22383
+ };
22384
+ }
22385
+
22257
22386
  const formExamples = {
22258
22387
  integer: (form, strings) => strings.integerExample,
22259
22388
  proper: (form, strings) => form.simplify === "optional" ? strings.properExample : strings.simplifiedProperExample,
@@ -22309,97 +22438,7 @@ class NumericInput extends React__namespace.Component {
22309
22438
  return answerStrings[0];
22310
22439
  }
22311
22440
  static validate(userInput, rubric, strings) {
22312
- const defaultAnswerForms = answerFormButtons.map(e => e["value"])
22313
- // Don't default to validating the answer as a pi answer
22314
- // if answerForm isn't set on the answer
22315
- // https://khanacademy.atlassian.net/browse/LC-691
22316
- .filter(e => e !== "pi");
22317
- const createValidator = answer => {
22318
- const stringAnswer = `${answer.value}`;
22319
-
22320
- // Always validate against the provided answer forms (pi, decimal, etc.)
22321
- const validatorForms = [...(answer.answerForms ?? [])];
22322
-
22323
- // When an answer is set to strict, we validate using ONLY
22324
- // the provided answerForms. If strict is false, or if there
22325
- // were no provided answer forms, we will include all
22326
- // of the default answer forms in our validator.
22327
- if (!answer.strict || validatorForms.length === 0) {
22328
- validatorForms.push(...defaultAnswerForms);
22329
- }
22330
- return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
22331
- message: answer.message,
22332
- simplify: answer.status === "correct" ? answer.simplify : "optional",
22333
- inexact: true,
22334
- // TODO(merlob) backfill / delete
22335
- maxError: answer.maxError,
22336
- forms: validatorForms
22337
- }, strings);
22338
- };
22339
-
22340
- // We may have received TeX; try to parse it before grading.
22341
- // If `currentValue` is not TeX, this should be a no-op.
22342
- const currentValue = ParseTex(userInput.currentValue);
22343
- const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22344
- const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
22345
-
22346
- // Look through all correct answers for one that matches either
22347
- // precisely or approximately and return the appropriate message:
22348
- // - if precise, return the message that the answer came with
22349
- // - if it needs to be simplified, etc., show that message
22350
- let result = correctAnswers.map(answer => {
22351
- // The coefficient is an attribute of the widget
22352
- let localValue = currentValue;
22353
- if (rubric.coefficient) {
22354
- if (!localValue) {
22355
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
22356
- localValue = 1;
22357
- } else if (localValue === "-") {
22358
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
22359
- localValue = -1;
22360
- }
22361
- }
22362
- const validate = createValidator(answer);
22363
- return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
22364
- }).find(match => match.correct || match.empty);
22365
- if (!result) {
22366
- // Otherwise, if the guess is not correct
22367
- const otherAnswers = [].concat(
22368
- // @ts-expect-error - TS2769 - No overload matches this call.
22369
- rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
22370
-
22371
- // Look through all other answers and if one matches either
22372
- // precisely or approximately return the answer's message
22373
- const match = otherAnswers.find(answer => {
22374
- const validate = createValidator(answer);
22375
- return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
22376
- });
22377
- result = {
22378
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22379
- empty: match ? match.status === "ungraded" : false,
22380
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22381
- correct: match ? match.status === "correct" : false,
22382
- // @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
22383
- message: match ? match.message : null,
22384
- guess: currentValue
22385
- };
22386
- }
22387
-
22388
- // TODO(eater): Seems silly to translate result to this
22389
- // invalid/points thing and immediately translate it
22390
- // back in ItemRenderer.scoreInput()
22391
- if (result.empty) {
22392
- return {
22393
- type: "invalid",
22394
- message: result.message
22395
- };
22396
- }
22397
- return {
22398
- type: "points",
22399
- earned: result.correct ? 1 : 0,
22400
- total: 1,
22401
- message: result.message
22402
- };
22441
+ return numericInputValidator(userInput, rubric, strings);
22403
22442
  }
22404
22443
  state = {
22405
22444
  // keeps track of the other set of values when switching
@@ -22434,7 +22473,7 @@ class NumericInput extends React__namespace.Component {
22434
22473
  return !noFormsAccepted && !allFormsAccepted;
22435
22474
  };
22436
22475
  simpleValidate = rubric => {
22437
- return NumericInput.validate(this.getUserInput(), rubric, this.context.strings);
22476
+ return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
22438
22477
  };
22439
22478
  focus = () => {
22440
22479
  this.inputRef?.focus();
@@ -22565,37 +22604,6 @@ const unionAnswerForms = function (answerFormsList) {
22565
22604
  return formExampleKeys.indexOf(form.name);
22566
22605
  });
22567
22606
  };
22568
- // This function checks if the user inputted a percent value, parsing
22569
- // it as a number (and maybe scaling) so that it can be graded.
22570
- // NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
22571
- // can accept several input forms with or without "%", the decision
22572
- // to parse based on the presence of "%" in the input, is so that we
22573
- // don't accidently scale the user typed value before grading, CP-930.
22574
- function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
22575
- // If the input value is not a string ending with "%", then there's
22576
- // nothing more to do. The value will be graded as inputted by user.
22577
- if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
22578
- return inputValue;
22579
- }
22580
- const value = parseFloat(inputValue.slice(0, -1));
22581
- // If the input value stripped of the "%" cannot be parsed as a
22582
- // number (the slice is not really necessary for parseFloat to work
22583
- // if the string starts with a number) then return the original
22584
- // input for grading.
22585
- if (isNaN(value)) {
22586
- return inputValue;
22587
- }
22588
-
22589
- // Next, if all correct answers are in the range of |0,1| then we
22590
- // scale the user typed value. We assume this is the correct thing
22591
- // to do since the input value ends with "%".
22592
- if (normalizedAnswerExpected) {
22593
- return value / 100;
22594
- }
22595
-
22596
- // Otherwise, we return input valuåe (number) stripped of the "%".
22597
- return value;
22598
- }
22599
22607
  const propsTransform = function (widgetOptions) {
22600
22608
  const rendererProps = ___default["default"].extend(___default["default"].omit(widgetOptions, "answers"), {
22601
22609
  answerForms: unionAnswerForms(
@@ -22623,6 +22631,23 @@ var NumericInput$1 = {
22623
22631
  isLintable: true
22624
22632
  };
22625
22633
 
22634
+ /**
22635
+ * Several widgets don't have "right"/"wrong" validation logic,
22636
+ * so this just says to move on past those widgets
22637
+ *
22638
+ * TODO: widgets that use this probably shouldn't have any
22639
+ * validation logic and the thing validating an exercise
22640
+ * should just know to skip these
22641
+ */
22642
+ function noopValidator() {
22643
+ return {
22644
+ type: "points",
22645
+ earned: 0,
22646
+ total: 0,
22647
+ message: null
22648
+ };
22649
+ }
22650
+
22626
22651
  const EN_DASH$1 = "\u2013";
22627
22652
  class PassageRef extends React__namespace.Component {
22628
22653
  static contextType = PerseusI18nContext;
@@ -22641,12 +22666,7 @@ class PassageRef extends React__namespace.Component {
22641
22666
  content: null
22642
22667
  };
22643
22668
  static validate(userInput, rubric) {
22644
- return {
22645
- type: "points",
22646
- earned: 0,
22647
- total: 0,
22648
- message: null
22649
- };
22669
+ return noopValidator();
22650
22670
  }
22651
22671
  componentDidMount() {
22652
22672
  // TODO(scottgrant): This is a hack to remove the deprecated call to
@@ -22747,6 +22767,7 @@ class PassageRef extends React__namespace.Component {
22747
22767
  var PassageRef$1 = {
22748
22768
  name: "passage-ref",
22749
22769
  displayName: "PassageRef (SAT only)",
22770
+ hidden: true,
22750
22771
  defaultAlignment: "inline",
22751
22772
  widget: PassageRef,
22752
22773
  transform: widgetOptions => ({
@@ -24655,23 +24676,6 @@ var Radio = {
24655
24676
  /* eslint-disable import/no-named-as-default */
24656
24677
  var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
24657
24678
 
24658
- /* Free implementation of getUserInput. This should be used sparingly, since it
24659
- * just returns all the widget's props rather than picking out those which were
24660
- * input by the user.
24661
- */
24662
- const WidgetJsonifyDeprecated = {
24663
- getUserInput: function () {
24664
- // Omit props that get passed to all widgets
24665
- // @ts-expect-error - TS2339 - Property 'props' does not exist on type '{ readonly getUserInput: () => any; readonly getUserInputFromProps: (props: any) => any; }'.
24666
- return ___default["default"].omit(this.props, denylist);
24667
- },
24668
- // Static version of `WidgetJsonifyDeprecated.getUserInput`
24669
- getUserInputFromProps: function (props) {
24670
- // Omit props that get passed to all widgets
24671
- return ___default["default"].omit(props, denylist);
24672
- }
24673
- };
24674
-
24675
24679
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
24676
24680
  class Categorizer extends React__namespace.Component {
24677
24681
  static contextType = PerseusI18nContext;
@@ -24709,7 +24713,9 @@ class Categorizer extends React__namespace.Component {
24709
24713
  };
24710
24714
  }
24711
24715
  static getUserInputFromProps(props) {
24712
- return WidgetJsonifyDeprecated.getUserInputFromProps(props);
24716
+ return {
24717
+ values: props.values
24718
+ };
24713
24719
  }
24714
24720
  change = (() => {
24715
24721
  var _this = this;
@@ -24864,6 +24870,7 @@ const styles$s = aphrodite.StyleSheet.create({
24864
24870
  var Categorizer$1 = {
24865
24871
  name: "categorizer",
24866
24872
  displayName: "Categorizer",
24873
+ hidden: true,
24867
24874
  widget: Categorizer,
24868
24875
  transform: widgetOptions => {
24869
24876
  return ___default["default"].pick(widgetOptions, "items", "categories", "randomizeItems");
@@ -24944,18 +24951,12 @@ function getUrlFromProgramID$1(programID) {
24944
24951
  /* This renders the scratchpad in an iframe and handles validation via
24945
24952
  * window.postMessage */
24946
24953
  class CSProgram extends React__namespace.Component {
24947
- static propTypes = {
24948
- ...propTypes,
24949
- programID: PropTypes__default["default"].string,
24950
- programType: PropTypes__default["default"].oneOf(["pjs", "sql", "webpage"]),
24951
- width: PropTypes__default["default"].number,
24952
- height: PropTypes__default["default"].number,
24953
- // eslint-disable-next-line react/forbid-prop-types
24954
- settings: PropTypes__default["default"].array,
24955
- showEditor: PropTypes__default["default"].bool,
24956
- showButtons: PropTypes__default["default"].bool,
24957
- status: PropTypes__default["default"].oneOf(["incomplete", "incorrect", "correct"]),
24958
- message: PropTypes__default["default"].string
24954
+ static defaultProps = {
24955
+ showEditor: false,
24956
+ showButtons: false,
24957
+ status: "incomplete",
24958
+ // optional message
24959
+ message: null
24959
24960
  };
24960
24961
 
24961
24962
  // The widget's grading function
@@ -24983,13 +24984,6 @@ class CSProgram extends React__namespace.Component {
24983
24984
  message: "Keep going, you're not there yet!"
24984
24985
  };
24985
24986
  }
24986
- static defaultProps = {
24987
- showEditor: false,
24988
- showButtons: false,
24989
- status: "incomplete",
24990
- // optional message
24991
- message: null
24992
- };
24993
24987
  componentDidMount() {
24994
24988
  $__default["default"](window).on("message", this.handleMessageEvent);
24995
24989
  }
@@ -25025,12 +25019,12 @@ class CSProgram extends React__namespace.Component {
25025
25019
  return change.apply(_this, args);
25026
25020
  };
25027
25021
  })();
25028
- simpleValidate = rubric => {
25022
+ simpleValidate(rubric) {
25029
25023
  return CSProgram.validate({
25030
25024
  status: this.props.status,
25031
25025
  message: this.props.message
25032
25026
  }, rubric);
25033
- };
25027
+ }
25034
25028
  render() {
25035
25029
  if (!this.props.programID) {
25036
25030
  return /*#__PURE__*/React__namespace.createElement("div", null);
@@ -25115,12 +25109,7 @@ class Definition extends React__namespace.Component {
25115
25109
  definition: "definition goes here"
25116
25110
  };
25117
25111
  static validate(userInput, rubric) {
25118
- return {
25119
- type: "points",
25120
- earned: 0,
25121
- total: 0,
25122
- message: null
25123
- };
25112
+ return noopValidator();
25124
25113
  }
25125
25114
  getUserInput = () => {
25126
25115
  return {};
@@ -25226,6 +25215,23 @@ var DeprecatedStandin$1 = {
25226
25215
  hidden: true
25227
25216
  };
25228
25217
 
25218
+ function dropdownValidator(userInput, rubric) {
25219
+ const selected = userInput.value;
25220
+ if (selected === 0) {
25221
+ return {
25222
+ type: "invalid",
25223
+ message: null
25224
+ };
25225
+ }
25226
+ const correct = rubric.choices[selected - 1].correct;
25227
+ return {
25228
+ type: "points",
25229
+ earned: correct ? 1 : 0,
25230
+ total: 1,
25231
+ message: null
25232
+ };
25233
+ }
25234
+
25229
25235
  class Dropdown extends React__namespace.Component {
25230
25236
  static defaultProps = {
25231
25237
  choices: [],
@@ -25234,20 +25240,7 @@ class Dropdown extends React__namespace.Component {
25234
25240
  apiOptions: ApiOptions.defaults
25235
25241
  };
25236
25242
  static validate(userInput, rubric) {
25237
- const selected = userInput.value;
25238
- if (selected === 0) {
25239
- return {
25240
- type: "invalid",
25241
- message: null
25242
- };
25243
- }
25244
- const correct = rubric.choices[selected - 1].correct;
25245
- return {
25246
- type: "points",
25247
- earned: correct ? 1 : 0,
25248
- total: 1,
25249
- message: null
25250
- };
25243
+ return dropdownValidator(userInput, rubric);
25251
25244
  }
25252
25245
  focus = () => {
25253
25246
  // TODO(LP-10797): This focus() call doesn't do anything because our
@@ -25272,7 +25265,7 @@ class Dropdown extends React__namespace.Component {
25272
25265
  };
25273
25266
  };
25274
25267
  simpleValidate = rubric => {
25275
- return Dropdown.validate(this.getUserInput(), rubric);
25268
+ return dropdownValidator(this.getUserInput(), rubric);
25276
25269
  };
25277
25270
  render() {
25278
25271
  const children = [/*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
@@ -25339,12 +25332,7 @@ class Explanation extends React__namespace.Component {
25339
25332
  linterContext: PerseusLinter.linterContextDefault
25340
25333
  };
25341
25334
  static validate(userInput, rubric) {
25342
- return {
25343
- type: "points",
25344
- earned: 0,
25345
- total: 0,
25346
- message: null
25347
- };
25335
+ return noopValidator();
25348
25336
  }
25349
25337
  componentDidMount() {
25350
25338
  this._mounted = true;
@@ -27208,6 +27196,7 @@ const staticTransform$4 = editorProps => {
27208
27196
  var Grapher$1 = {
27209
27197
  name: "grapher",
27210
27198
  displayName: "Grapher",
27199
+ hidden: true,
27211
27200
  widget: Grapher,
27212
27201
  transform: propTransform$2,
27213
27202
  staticTransform: staticTransform$4
@@ -27355,28 +27344,23 @@ var Group$1 = {
27355
27344
  displayName: "Group (SAT only)",
27356
27345
  widget: Group,
27357
27346
  traverseChildWidgets: traverseChildWidgets,
27358
- hidden: false,
27347
+ hidden: true,
27359
27348
  isLintable: true
27360
27349
  };
27361
27350
 
27362
- /* eslint-disable react/forbid-prop-types */
27351
+ /**
27352
+ * This is an iframe widget. It is used for rendering an iframe that
27353
+ * then communicates its state via window.postMessage
27354
+ * This is useful for embedding arbitrary visualizations/simulations with
27355
+ * completed conditions, such as the mazes and games in Algorithms.
27356
+ * It's particularly well suited for embedding our ProcessingJS programs,
27357
+ * but could also be used for embedding viz's hosted elsewhere.
27358
+ */
27363
27359
  const {
27364
27360
  updateQueryString
27365
27361
  } = Util;
27366
-
27367
27362
  /* This renders the iframe and handles validation via window.postMessage */
27368
27363
  class Iframe extends React__namespace.Component {
27369
- static propTypes = {
27370
- ...propTypes,
27371
- width: PropTypes__default["default"].oneOfType([PropTypes__default["default"].string, PropTypes__default["default"].number]),
27372
- height: PropTypes__default["default"].oneOfType([PropTypes__default["default"].string, PropTypes__default["default"].number]),
27373
- url: PropTypes__default["default"].string,
27374
- settings: PropTypes__default["default"].array,
27375
- status: PropTypes__default["default"].oneOf(["incomplete", "incorrect", "correct"]),
27376
- message: PropTypes__default["default"].string,
27377
- allowFullScreen: PropTypes__default["default"].bool,
27378
- allowTopNavigation: PropTypes__default["default"].bool
27379
- };
27380
27364
  static defaultProps = {
27381
27365
  status: "incomplete",
27382
27366
  // optional message
@@ -27390,9 +27374,12 @@ class Iframe extends React__namespace.Component {
27390
27374
  componentWillUnmount() {
27391
27375
  $__default["default"](window).off("message", this.handleMessageEvent);
27392
27376
  }
27393
- getUserInput = () => {
27394
- return WidgetJsonifyDeprecated.getUserInput.call(this);
27395
- };
27377
+ getUserInput() {
27378
+ return {
27379
+ status: this.props.status,
27380
+ message: this.props.message
27381
+ };
27382
+ }
27396
27383
  handleMessageEvent = e => {
27397
27384
  // We receive data from the iframe that contains {passed: true/false}
27398
27385
  // and use that to set the status
@@ -27552,12 +27539,7 @@ class ImageWidget extends React__namespace.Component {
27552
27539
  linterContext: PerseusLinter.linterContextDefault
27553
27540
  };
27554
27541
  static validate(userInput, rubric) {
27555
- return {
27556
- type: "points",
27557
- earned: 0,
27558
- total: 0,
27559
- message: null
27560
- };
27542
+ return noopValidator();
27561
27543
  }
27562
27544
  change = (() => {
27563
27545
  var _this = this;
@@ -27800,12 +27782,7 @@ class Interaction extends React__namespace.Component {
27800
27782
  elements: []
27801
27783
  };
27802
27784
  static validate(state, rubric) {
27803
- return {
27804
- type: "points",
27805
- earned: 0,
27806
- total: 0,
27807
- message: null
27808
- };
27785
+ return noopValidator();
27809
27786
  }
27810
27787
  state = {
27811
27788
  variables: _getInitialVariables(this.props.elements),
@@ -29977,6 +29954,7 @@ function LockedLabel(props) {
29977
29954
 
29978
29955
  function GraphLockedLabelsLayer(props) {
29979
29956
  const {
29957
+ flags,
29980
29958
  lockedFigures
29981
29959
  } = props;
29982
29960
  return lockedFigures.map((figure, i) => {
@@ -29985,9 +29963,13 @@ function GraphLockedLabelsLayer(props) {
29985
29963
  key: `label-${i}`
29986
29964
  }, figure));
29987
29965
  }
29988
-
29989
- // TODO(LEMS-2271): Add support for labels within
29990
- // other locked figure types
29966
+ if (flags?.["mafs"]?.["locked-point-labels"] && figure.type === "point") {
29967
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
29968
+ key: `point-${i}`
29969
+ }, figure.labels.map((label, j) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
29970
+ key: `locked-figure-${i}-label-${j}`
29971
+ }, label))));
29972
+ }
29991
29973
  return null;
29992
29974
  });
29993
29975
  }
@@ -32324,7 +32306,8 @@ const MafsGraph = props => {
32324
32306
  props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
32325
32307
  lockedFigures: props.lockedFigures,
32326
32308
  range: state.range
32327
- }))), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
32309
+ }))), props.flags?.["mafs"]?.["interactive-graph-locked-features-labels"] && props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
32310
+ flags: props.flags,
32328
32311
  lockedFigures: props.lockedFigures
32329
32312
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
32330
32313
  style: {
@@ -34960,7 +34943,7 @@ class InteractiveGraph extends React__namespace.Component {
34960
34943
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
34961
34944
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
34962
34945
  return /*#__PURE__*/React__namespace.createElement(StatefulMafsGraph, _extends({}, this.props, {
34963
- showLabelsFlag: this.props.apiOptions?.flags?.["mafs"]?.["interactive-graph-locked-features-labels"],
34946
+ flags: this.props.apiOptions?.flags,
34964
34947
  ref: this.mafsRef,
34965
34948
  gridStep: gridStep,
34966
34949
  snapStep: snapStep,
@@ -35557,7 +35540,7 @@ function shouldUseMafs(mafsFlags, graph) {
35557
35540
  const staticTransform$3 = ___default["default"].identity;
35558
35541
  var InteractiveGraph$1 = {
35559
35542
  name: "interactive-graph",
35560
- displayName: "Interactive graph",
35543
+ displayName: "Interactive graph (Assessments only)",
35561
35544
  widget: InteractiveGraph,
35562
35545
  staticTransform: staticTransform$3
35563
35546
  };
@@ -37511,11 +37494,6 @@ var LabelImage$1 = {
37511
37494
  };
37512
37495
 
37513
37496
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
37514
- let Layout = /*#__PURE__*/function (Layout) {
37515
- Layout["HORIZONTAL"] = "horizontal";
37516
- Layout["VERTICAL"] = "vertical";
37517
- return Layout;
37518
- }({});
37519
37497
  var ItemState = /*#__PURE__*/function (ItemState) {
37520
37498
  ItemState["STATIC"] = "static";
37521
37499
  ItemState["DRAGGING"] = "dragging";
@@ -37542,7 +37520,7 @@ class Placeholder extends React__namespace.Component {
37542
37520
  const {
37543
37521
  layout
37544
37522
  } = this.props;
37545
- const className = aphrodite.css(styles$d.card, styles$d.placeholder, layout === Layout.HORIZONTAL && styles$d.horizontalCard);
37523
+ const className = aphrodite.css(styles$d.card, styles$d.placeholder, layout === "horizontal" && styles$d.horizontalCard);
37546
37524
  const style = {
37547
37525
  width: this.props.width,
37548
37526
  height: this.props.height
@@ -37749,7 +37727,7 @@ class Draggable extends React__namespace.Component {
37749
37727
  // We need to keep backwards compatbility with rules specified directly
37750
37728
  // in CSS. Hence the hacky tacking on of manual classNames.
37751
37729
  // See sortable.less for details.
37752
- let className = aphrodite.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";
37730
+ let className = aphrodite.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";
37753
37731
  if (!includePadding) {
37754
37732
  className += " perseus-sortable-draggable-unpadded";
37755
37733
  }
@@ -37789,7 +37767,7 @@ class Draggable extends React__namespace.Component {
37789
37767
  }
37790
37768
  class Sortable extends React__namespace.Component {
37791
37769
  static defaultProps = {
37792
- layout: Layout.HORIZONTAL,
37770
+ layout: "horizontal",
37793
37771
  padding: true,
37794
37772
  disabled: false,
37795
37773
  constraints: {},
@@ -37884,7 +37862,7 @@ class Sortable extends React__namespace.Component {
37884
37862
  if (constraints?.width) {
37885
37863
  // Items must be at least as wide as the specified constraint
37886
37864
  syncWidth = ___default["default"].max(widths.concat(constraints.width));
37887
- } else if (layout === Layout.VERTICAL) {
37865
+ } else if (layout === "vertical") {
37888
37866
  // Sync widths to get a clean column
37889
37867
  syncWidth = ___default["default"].max(widths);
37890
37868
  }
@@ -37892,7 +37870,7 @@ class Sortable extends React__namespace.Component {
37892
37870
  if (constraints?.height) {
37893
37871
  // Items must be at least as high as the specified constraint
37894
37872
  syncHeight = ___default["default"].max(heights.concat(constraints.height));
37895
- } else if (layout === Layout.HORIZONTAL) {
37873
+ } else if (layout === "horizontal") {
37896
37874
  // Sync widths to get a clean row
37897
37875
  syncHeight = ___default["default"].max(heights);
37898
37876
  }
@@ -37974,7 +37952,7 @@ class Sortable extends React__namespace.Component {
37974
37952
 
37975
37953
  // @ts-expect-error - TS2551 - Property 'splice' does not exist on type 'readonly SortableItem[]'. Did you mean 'slice'?
37976
37954
  items.splice(currentIndex, 1);
37977
- if (this.props.layout === Layout.HORIZONTAL) {
37955
+ if (this.props.layout === "horizontal") {
37978
37956
  // @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'. | TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
37979
37957
  const midWidth = $draggable.offset().left - $sortable.offset().left;
37980
37958
  let sumWidth = 0;
@@ -38088,19 +38066,19 @@ class Sortable extends React__namespace.Component {
38088
38066
  // We need to keep backwards compatbility with rules specified directly
38089
38067
  // in CSS. See sortable.less for details.
38090
38068
  const className = aphrodite.css(styles$d.sortable) + " perseus-sortable";
38091
- const syncWidth = this.props.constraints?.width || layout === Layout.VERTICAL;
38092
- const syncHeight = this.props.constraints?.height || layout === Layout.HORIZONTAL;
38069
+ const syncWidth = this.props.constraints?.width || layout === "vertical";
38070
+ const syncHeight = this.props.constraints?.height || layout === "horizontal";
38093
38071
  ___default["default"].each(this.state.items, function (item, i, items) {
38094
38072
  const isLast = i === items.length - 1;
38095
38073
  const isStatic = item.state === ItemState.STATIC || item.state === ItemState.DISABLED;
38096
38074
  let margin;
38097
38075
 
38098
38076
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38099
- if (this.props.layout === Layout.HORIZONTAL) {
38077
+ if (this.props.layout === "horizontal") {
38100
38078
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38101
38079
  margin = "0 " + this.props.margin + "px 0 0"; // right
38102
38080
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38103
- } else if (this.props.layout === Layout.VERTICAL) {
38081
+ } else if (this.props.layout === "vertical") {
38104
38082
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
38105
38083
  margin = "0 0 " + this.props.margin + "px 0"; // bottom
38106
38084
  }
@@ -38222,7 +38200,6 @@ const styles$d = aphrodite.StyleSheet.create({
38222
38200
  }
38223
38201
  });
38224
38202
 
38225
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
38226
38203
  const {
38227
38204
  shuffle: shuffle$1,
38228
38205
  seededRNG
@@ -38230,18 +38207,6 @@ const {
38230
38207
  const HACKY_CSS_CLASSNAME = "perseus-widget-matcher";
38231
38208
  class Matcher extends React__namespace.Component {
38232
38209
  static contextType = PerseusI18nContext;
38233
- static propTypes = {
38234
- apiOptions: ApiOptions.propTypes,
38235
- labels: PropTypes__default["default"].array,
38236
- left: PropTypes__default["default"].array,
38237
- onChange: PropTypes__default["default"].func,
38238
- orderMatters: PropTypes__default["default"].bool,
38239
- padding: PropTypes__default["default"].bool,
38240
- problemNum: PropTypes__default["default"].number,
38241
- right: PropTypes__default["default"].array,
38242
- trackInteraction: PropTypes__default["default"].func.isRequired,
38243
- linterContext: PerseusLinter.linterContextProps
38244
- };
38245
38210
  static defaultProps = {
38246
38211
  left: [],
38247
38212
  right: [],
@@ -38378,7 +38343,7 @@ class Matcher extends React__namespace.Component {
38378
38343
  className: aphrodite.css(styles$c.column)
38379
38344
  }, /*#__PURE__*/React__namespace.createElement(Sortable, {
38380
38345
  options: left,
38381
- layout: Layout.VERTICAL,
38346
+ layout: "vertical",
38382
38347
  padding: this.props.padding,
38383
38348
  disabled: !this.props.orderMatters,
38384
38349
  constraints: constraints,
@@ -38391,11 +38356,9 @@ class Matcher extends React__namespace.Component {
38391
38356
  ref: "left"
38392
38357
  })), /*#__PURE__*/React__namespace.createElement("td", {
38393
38358
  className: aphrodite.css(styles$c.column, styles$c.columnRight)
38394
- }, /*#__PURE__*/React__namespace.createElement(Sortable
38395
- // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
38396
- , {
38359
+ }, /*#__PURE__*/React__namespace.createElement(Sortable, {
38397
38360
  options: right,
38398
- layout: Layout.VERTICAL,
38361
+ layout: "vertical",
38399
38362
  padding: this.props.padding,
38400
38363
  constraints: constraints,
38401
38364
  onMeasure: this.onMeasureRight,
@@ -38916,6 +38879,7 @@ const staticTransform$2 = editorProps => {
38916
38879
  var Matrix$1 = {
38917
38880
  name: "matrix",
38918
38881
  displayName: "Matrix",
38882
+ hidden: true,
38919
38883
  widget: Matrix,
38920
38884
  transform: propTransform$1,
38921
38885
  staticTransform: staticTransform$2,
@@ -39072,6 +39036,7 @@ const propUpgrades = {
39072
39036
  var Measurer$1 = {
39073
39037
  name: "measurer",
39074
39038
  displayName: "Measurer",
39039
+ hidden: true,
39075
39040
  widget: Measurer,
39076
39041
  version: {
39077
39042
  major: 1,
@@ -39964,12 +39929,7 @@ class MoleculeWidget extends React__namespace.Component {
39964
39929
  // TODO(colin): this is here as part of the interface for a component.
39965
39930
  // Figure out if there is something more appropriate that this should
39966
39931
  // return.
39967
- return {
39968
- type: "points",
39969
- earned: 0,
39970
- total: 0,
39971
- message: null
39972
- };
39932
+ return noopValidator();
39973
39933
  };
39974
39934
  render() {
39975
39935
  return /*#__PURE__*/React__namespace.createElement(Molecule, {
@@ -40569,10 +40529,6 @@ var NumberLine$1 = {
40569
40529
  };
40570
40530
 
40571
40531
  class PlaceholderCard extends React__namespace.Component {
40572
- static propTypes = {
40573
- width: PropTypes__default["default"].number.isRequired,
40574
- height: PropTypes__default["default"].number.isRequired
40575
- };
40576
40532
  render() {
40577
40533
  return /*#__PURE__*/React__namespace.createElement("div", {
40578
40534
  className: "card-wrap " + ClassNames.INTERACTIVE,
@@ -40582,7 +40538,7 @@ class PlaceholderCard extends React__namespace.Component {
40582
40538
  }, /*#__PURE__*/React__namespace.createElement("div", {
40583
40539
  className: "card placeholder",
40584
40540
  style: {
40585
- height: this.props.height
40541
+ height: this.props.width
40586
40542
  }
40587
40543
  }));
40588
40544
  }
@@ -40596,29 +40552,10 @@ class DragHintCard extends React__namespace.Component {
40596
40552
  }));
40597
40553
  }
40598
40554
  }
40599
- const PropTypePosition = PropTypes__default["default"].shape({
40600
- left: PropTypes__default["default"].number,
40601
- top: PropTypes__default["default"].number
40602
- });
40603
40555
  class Card extends React__namespace.Component {
40604
40556
  static contextType = PerseusI18nContext;
40605
40557
  // @ts-expect-error - TS2564 - Property 'mouseMoveUpBound' has no initializer and is not definitely assigned in the constructor.
40606
40558
  mouseMoveUpBound;
40607
- static propTypes = {
40608
- floating: PropTypes__default["default"].bool.isRequired,
40609
- animating: PropTypes__default["default"].bool,
40610
- width: PropTypes__default["default"].number,
40611
- stack: PropTypes__default["default"].bool,
40612
- onMouseDown: PropTypes__default["default"].func,
40613
- onMouseMove: PropTypes__default["default"].func,
40614
- onMouseUp: PropTypes__default["default"].func,
40615
- // Used only for floating/animating cards
40616
- startMouse: PropTypePosition,
40617
- startOffset: PropTypePosition,
40618
- animateTo: PropTypePosition,
40619
- onAnimationEnd: PropTypes__default["default"].func,
40620
- linterContext: PerseusLinter.linterContextProps
40621
- };
40622
40559
  static defaultProps = {
40623
40560
  stack: false,
40624
40561
  animating: false,
@@ -40656,7 +40593,7 @@ class Card extends React__namespace.Component {
40656
40593
  this.props.fakeRef !== nextProps.fakeRef;
40657
40594
  }
40658
40595
  componentDidUpdate(prevProps, prevState) {
40659
- if (this.props.animating && !prevProps.animating) {
40596
+ if (this.props.animating && !prevProps.animating && this.props.animateTo && this.props.startOffset) {
40660
40597
  // If we just were changed into animating, start the animation.
40661
40598
  // We pick the animation speed based on the distance that the card
40662
40599
  // needs to travel. (Why sqrt? Just because it looks nice -- with a
@@ -40723,8 +40660,8 @@ class Card extends React__namespace.Component {
40723
40660
  if (this.props.floating) {
40724
40661
  style = {
40725
40662
  position: "absolute",
40726
- left: this.props.startOffset.left,
40727
- top: this.props.startOffset.top
40663
+ left: this.props.startOffset?.left,
40664
+ top: this.props.startOffset?.top
40728
40665
  };
40729
40666
  }
40730
40667
  if (this.props.width) {
@@ -40734,7 +40671,7 @@ class Card extends React__namespace.Component {
40734
40671
  if (this.props.stack) {
40735
40672
  className.push("stack");
40736
40673
  }
40737
- if (this.props.floating && !this.props.animating) {
40674
+ if (this.props.floating && !this.props.animating && this.props.mouse && this.props.startMouse) {
40738
40675
  className.push("dragging");
40739
40676
  style.left += this.props.mouse.left - this.props.startMouse.left;
40740
40677
  style.top += this.props.mouse.top - this.props.startMouse.top;
@@ -40759,14 +40696,14 @@ class Card extends React__namespace.Component {
40759
40696
  }
40760
40697
  }
40761
40698
  const NORMAL = "normal",
40762
- HORIZONTAL$1 = "horizontal";
40699
+ HORIZONTAL = "horizontal";
40763
40700
  class Orderer extends React__namespace.Component {
40764
40701
  static defaultProps = {
40765
40702
  current: [],
40766
40703
  options: [],
40767
40704
  correctOptions: [],
40768
40705
  height: NORMAL,
40769
- layout: HORIZONTAL$1,
40706
+ layout: HORIZONTAL,
40770
40707
  linterContext: PerseusLinter.linterContextDefault
40771
40708
  };
40772
40709
  state = {
@@ -40919,7 +40856,7 @@ class Orderer extends React__namespace.Component {
40919
40856
  };
40920
40857
  findCorrectIndex = (draggable, list) => {
40921
40858
  // Find the correct index for a card given the current cards.
40922
- const isHorizontal = this.props.layout === HORIZONTAL$1;
40859
+ const isHorizontal = this.props.layout === HORIZONTAL;
40923
40860
  // eslint-disable-next-line react/no-string-refs
40924
40861
  // @ts-expect-error - TS2769 - No overload matches this call.
40925
40862
  const $dragList = $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.dragList));
@@ -40969,7 +40906,7 @@ class Orderer extends React__namespace.Component {
40969
40906
  if (draggable == null) {
40970
40907
  return false;
40971
40908
  }
40972
- const isHorizontal = this.props.layout === HORIZONTAL$1,
40909
+ const isHorizontal = this.props.layout === HORIZONTAL,
40973
40910
  // @ts-expect-error - TS2769 - No overload matches this call.
40974
40911
  $draggable = $__default["default"](ReactDOM__default["default"].findDOMNode(draggable)),
40975
40912
  // eslint-disable-next-line react/no-string-refs
@@ -41149,6 +41086,7 @@ ___default["default"].extend(Orderer, {
41149
41086
  var Orderer$1 = {
41150
41087
  name: "orderer",
41151
41088
  displayName: "Orderer",
41089
+ hidden: true,
41152
41090
  widget: Orderer,
41153
41091
  isLintable: true
41154
41092
  };
@@ -42959,12 +42897,7 @@ class Passage extends React__namespace.Component {
42959
42897
  stylesAreApplied: false
42960
42898
  };
42961
42899
  static validate(state, rubric) {
42962
- return {
42963
- type: "points",
42964
- earned: 0,
42965
- total: 0,
42966
- message: null
42967
- };
42900
+ return noopValidator();
42968
42901
  }
42969
42902
  componentDidMount() {
42970
42903
  this._updateState();
@@ -43325,6 +43258,7 @@ class Passage extends React__namespace.Component {
43325
43258
  var Passage$1 = {
43326
43259
  name: "passage",
43327
43260
  displayName: "Passage (SAT only)",
43261
+ hidden: true,
43328
43262
  widget: Passage,
43329
43263
  transform: editorProps => {
43330
43264
  return ___default["default"].pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
@@ -43340,16 +43274,15 @@ class PassageRefTarget extends React__namespace.Component {
43340
43274
  linterContext: PerseusLinter.linterContextDefault
43341
43275
  };
43342
43276
  static validate(state, rubric) {
43343
- return {
43344
- type: "points",
43345
- earned: 0,
43346
- total: 0,
43347
- message: null
43348
- };
43277
+ return noopValidator();
43349
43278
  }
43279
+
43280
+ // TODO passage-ref-target isn't interactive; remove
43350
43281
  getUserInput = () => {
43351
- return WidgetJsonifyDeprecated.getUserInput.call(this);
43282
+ return;
43352
43283
  };
43284
+
43285
+ // TODO passage-ref-target isn't interactive; remove
43353
43286
  change = (() => {
43354
43287
  var _this = this;
43355
43288
  return function () {
@@ -43359,6 +43292,8 @@ class PassageRefTarget extends React__namespace.Component {
43359
43292
  return change.apply(_this, args);
43360
43293
  };
43361
43294
  })();
43295
+
43296
+ // TODO passage-ref-target isn't interactive; remove
43362
43297
  simpleValidate = rubric => {
43363
43298
  return PassageRefTarget.validate(this.getUserInput(), rubric);
43364
43299
  };
@@ -44417,6 +44352,7 @@ const staticTransform = ___default["default"].identity;
44417
44352
  var Plotter$1 = {
44418
44353
  name: "plotter",
44419
44354
  displayName: "Plotter",
44355
+ hidden: true,
44420
44356
  widget: Plotter,
44421
44357
  staticTransform: staticTransform
44422
44358
  };
@@ -44467,29 +44403,14 @@ var PythonProgram$1 = {
44467
44403
  widget: PythonProgram
44468
44404
  };
44469
44405
 
44470
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
44471
44406
  const {
44472
44407
  shuffle
44473
44408
  } = Util;
44474
- const HORIZONTAL = "horizontal";
44475
- const VERTICAL = "vertical";
44476
44409
  class Sorter extends React__namespace.Component {
44477
- // @ts-expect-error - TS2564 - Property '_isMounted' has no initializer and is not definitely assigned in the constructor.
44478
- _isMounted;
44479
- static propTypes = {
44480
- apiOptions: ApiOptions.propTypes,
44481
- // eslint-disable-next-line react/forbid-prop-types
44482
- correct: PropTypes__default["default"].array,
44483
- layout: PropTypes__default["default"].oneOf([HORIZONTAL, VERTICAL]),
44484
- onChange: PropTypes__default["default"].func,
44485
- padding: PropTypes__default["default"].bool,
44486
- problemNum: PropTypes__default["default"].number,
44487
- trackInteraction: PropTypes__default["default"].func.isRequired,
44488
- linterContext: PerseusLinter.linterContextProps
44489
- };
44410
+ _isMounted = false;
44490
44411
  static defaultProps = {
44491
44412
  correct: [],
44492
- layout: HORIZONTAL,
44413
+ layout: "horizontal",
44493
44414
  padding: true,
44494
44415
  problemNum: 0,
44495
44416
  onChange: function () {},
@@ -44567,9 +44488,7 @@ class Sorter extends React__namespace.Component {
44567
44488
  const marginPx = apiOptions.isMobile ? 8 : 5;
44568
44489
  return /*#__PURE__*/React__namespace.createElement("div", {
44569
44490
  className: "perseus-widget-sorter perseus-clearfix"
44570
- }, /*#__PURE__*/React__namespace.createElement(Sortable
44571
- // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
44572
- , {
44491
+ }, /*#__PURE__*/React__namespace.createElement(Sortable, {
44573
44492
  options: options,
44574
44493
  layout: this.props.layout,
44575
44494
  margin: marginPx,
@@ -44592,7 +44511,6 @@ var Sorter$1 = {
44592
44511
  const {
44593
44512
  assert
44594
44513
  } = InteractiveUtil;
44595
-
44596
44514
  /* Input handling: Maps a (row, column) pair to a unique ref used by React,
44597
44515
  * and extracts (row, column) pairs from input paths, used to allow outsiders
44598
44516
  * to focus, blur, set input values, etc. */
@@ -44619,16 +44537,6 @@ const getRefForPath = function (path) {
44619
44537
  };
44620
44538
  class Table extends React__namespace.Component {
44621
44539
  static contextType = PerseusI18nContext;
44622
- static propTypes = {
44623
- answers: PropTypes__default["default"].arrayOf(PropTypes__default["default"].arrayOf(PropTypes__default["default"].string)),
44624
- editableHeaders: PropTypes__default["default"].bool,
44625
- // The editor to use when editableHeaders is enabled
44626
- Editor: PropTypes__default["default"].func,
44627
- headers: PropTypes__default["default"].arrayOf(PropTypes__default["default"].string),
44628
- keypadElement: mathInput.keypadElementPropType,
44629
- trackInteraction: PropTypes__default["default"].func.isRequired,
44630
- linterContext: PerseusLinter.linterContextProps
44631
- };
44632
44540
  static defaultProps = function () {
44633
44541
  const defaultRows = 4;
44634
44542
  const defaultColumns = 1;
@@ -44974,12 +44882,7 @@ class Video extends React__namespace.Component {
44974
44882
  * of Khan Academy videos.
44975
44883
  */
44976
44884
  static validate(userInput, rubric) {
44977
- return {
44978
- type: "points",
44979
- earned: 0,
44980
- total: 0,
44981
- message: null
44982
- };
44885
+ return noopValidator();
44983
44886
  }
44984
44887
  getUserInput = () => {
44985
44888
  return null;
@@ -45099,7 +45002,7 @@ const init = function (options) {
45099
45002
 
45100
45003
  // This file is processed by a Rollup plugin (replace) to inject the production
45101
45004
  const libName = "@khanacademy/perseus";
45102
- const libVersion = "33.3.0";
45005
+ const libVersion = "34.0.0";
45103
45006
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45104
45007
 
45105
45008
  const apiVersion = {
@@ -48614,7 +48517,6 @@ exports.SizingUtils = sizingUtils;
48614
48517
  exports.TableWidget = Table$1;
48615
48518
  exports.Util = Util;
48616
48519
  exports.WIDGET_PROP_DENYLIST = denylist;
48617
- exports.WidgetJsonifyDeprecated = WidgetJsonifyDeprecated;
48618
48520
  exports.Widgets = widgets$1;
48619
48521
  exports.addWidget = addWidget;
48620
48522
  exports.apiVersion = apiVersion;