@instructure/canvas-rce 6.0.0 → 7.2.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 (120) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/__tests__/common/indicate.test.js +5 -6
  3. package/es/bridge/Bridge.js +2 -4
  4. package/es/canvasFileBrowser/FileBrowser.js +2 -4
  5. package/es/common/browser.js +2 -2
  6. package/es/common/fileUrl.js +13 -3
  7. package/es/defaultTinymceConfig.d.ts +1 -1
  8. package/es/defaultTinymceConfig.js +149 -114
  9. package/es/enhance-user-content/enhance_user_content.js +7 -1
  10. package/es/enhance-user-content/instructure_helper.js +4 -0
  11. package/es/enhance-user-content/youtube_overlay.d.ts +1 -0
  12. package/es/enhance-user-content/youtube_overlay.js +87 -0
  13. package/es/format-message.d.js +1 -0
  14. package/es/format-message.js +5 -0
  15. package/es/index.d.ts +1 -1
  16. package/es/rce/RCE.d.ts +0 -1
  17. package/es/rce/RCE.js +5 -10
  18. package/es/rce/RCEGlobals.d.ts +0 -2
  19. package/es/rce/RCEGlobals.js +0 -1
  20. package/es/rce/RCEVariants.d.ts +8 -3
  21. package/es/rce/RCEVariants.js +36 -10
  22. package/es/rce/RCEWrapper.d.ts +3 -5
  23. package/es/rce/RCEWrapper.js +68 -83
  24. package/es/rce/RCEWrapperProps.d.ts +1 -1
  25. package/es/rce/ShowOnFocusButton/index.js +4 -2
  26. package/es/rce/StatusBar.js +61 -15
  27. package/es/rce/alertHandler.js +6 -7
  28. package/es/rce/plugins/instructure-ui-icons/plugin.js +2 -2
  29. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.d.ts +1 -1
  30. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +6 -10
  31. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.d.ts +5 -15
  32. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.js +4 -10
  33. package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +7 -0
  34. package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +49 -9
  35. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.d.ts +1 -8
  36. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +13 -33
  37. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -2
  38. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.js +2 -1
  39. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +1 -2
  40. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +2 -4
  41. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +10 -7
  42. package/es/rce/plugins/shared/ContentSelection.js +4 -7
  43. package/es/rce/plugins/shared/DimensionsInput/index.js +3 -3
  44. package/es/rce/plugins/shared/FixedContentTray.d.ts +7 -23
  45. package/es/rce/plugins/shared/FixedContentTray.js +7 -16
  46. package/es/rce/plugins/shared/ImageCropper/constants.d.ts +1 -1
  47. package/es/rce/plugins/shared/ImageCropper/constants.js +1 -1
  48. package/es/rce/plugins/shared/ImageCropper/controls/CustomNumberInput.js +1 -1
  49. package/es/rce/plugins/shared/PreviewIcon.js +1 -1
  50. package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +3 -3
  51. package/es/rce/plugins/shared/ai_tools/aiicons.js +11 -11
  52. package/es/rce/plugins/shared/fileTypeUtils.js +1 -1
  53. package/es/rce/plugins/tinymce-a11y-checker/components/checker.js +7 -1
  54. package/es/rce/plugins/tinymce-a11y-checker/plugin.js +50 -52
  55. package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.d.ts +1 -1
  56. package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.js +1 -1
  57. package/es/rce/style.js +47 -45
  58. package/es/rcs/api.d.ts +4 -1
  59. package/es/rcs/api.js +9 -13
  60. package/es/translations/locales/ar.js +29 -5
  61. package/es/translations/locales/ca.js +32 -8
  62. package/es/translations/locales/cy.js +29 -5
  63. package/es/translations/locales/da-x-k12.js +29 -5
  64. package/es/translations/locales/da.js +29 -5
  65. package/es/translations/locales/de.js +29 -5
  66. package/es/translations/locales/en-AU-x-unimelb.js +29 -5
  67. package/es/translations/locales/en-GB-x-ukhe.js +29 -5
  68. package/es/translations/locales/en.js +20 -5
  69. package/es/translations/locales/en_AU.js +29 -5
  70. package/es/translations/locales/en_CA.js +29 -5
  71. package/es/translations/locales/en_CY.js +29 -5
  72. package/es/translations/locales/en_GB.js +29 -5
  73. package/es/translations/locales/es.js +29 -5
  74. package/es/translations/locales/es_ES.js +29 -5
  75. package/es/translations/locales/fa_IR.js +0 -3
  76. package/es/translations/locales/fi.js +29 -5
  77. package/es/translations/locales/fr.js +29 -5
  78. package/es/translations/locales/fr_CA.js +30 -6
  79. package/es/translations/locales/ga.js +46 -22
  80. package/es/translations/locales/hi.js +29 -5
  81. package/es/translations/locales/ht.js +29 -5
  82. package/es/translations/locales/hu.js +0 -6
  83. package/es/translations/locales/id.js +29 -5
  84. package/es/translations/locales/is.js +23 -5
  85. package/es/translations/locales/it.js +29 -5
  86. package/es/translations/locales/ja.js +29 -5
  87. package/es/translations/locales/mi.js +29 -5
  88. package/es/translations/locales/ms.js +29 -5
  89. package/es/translations/locales/nb-x-k12.js +29 -5
  90. package/es/translations/locales/nb.js +29 -5
  91. package/es/translations/locales/nl.js +29 -5
  92. package/es/translations/locales/nn.js +0 -6
  93. package/es/translations/locales/pl.js +29 -5
  94. package/es/translations/locales/pt.js +29 -5
  95. package/es/translations/locales/pt_BR.js +29 -5
  96. package/es/translations/locales/ru.js +29 -5
  97. package/es/translations/locales/sl.js +29 -5
  98. package/es/translations/locales/sv-x-k12.js +29 -5
  99. package/es/translations/locales/sv.js +29 -5
  100. package/es/translations/locales/th.js +29 -5
  101. package/es/translations/locales/uk_UA.js +0 -6
  102. package/es/translations/locales/vi.js +29 -5
  103. package/es/translations/locales/zh-Hans.js +29 -5
  104. package/es/translations/locales/zh-Hant.js +29 -5
  105. package/es/translations/locales/zh.js +29 -5
  106. package/es/translations/locales/zh_HK.js +29 -5
  107. package/es/util/contextHelper.d.ts +7 -0
  108. package/{testcafe/axe.test.js → es/util/contextHelper.js} +10 -21
  109. package/es/util/loadingPlaceholder.js +11 -11
  110. package/eslint.config.js +3 -25
  111. package/jest/jest-setup.js +27 -2
  112. package/jest.config.js +5 -1
  113. package/package.json +62 -85
  114. package/testcafe/RCEWrapper.test.js +0 -319
  115. package/testcafe/StatusBar.test.js +0 -108
  116. package/testcafe/enhanceUserContent.html +0 -58
  117. package/testcafe/enhanceUserContent.test.js +0 -44
  118. package/testcafe/entry.jsx +0 -77
  119. package/testcafe/testcafe.html +0 -14
  120. package/webpack.testcafe.config.js +0 -61
@@ -19,6 +19,7 @@ import _pt from "prop-types";
19
19
 
20
20
  import React, { Suspense } from 'react';
21
21
  import { Editor } from '@tinymce/tinymce-react';
22
+ import tinymce from 'tinymce';
22
23
  import _ from 'lodash';
23
24
  import { StoreProvider } from './plugins/shared/StoreContext';
24
25
  import { IconKeyboardShortcutsLine } from '@instructure/ui-icons';
@@ -64,6 +65,7 @@ import { getMenubarForVariant, getMenuForVariant, getToolbarForVariant, getStatu
64
65
  import { focusFirstMenuButton, focusToolbar, isElementWithinTable, mergeMenu, mergeMenuItems, mergePlugins, mergeToolbar, parsePluginsToExclude, patchAutosavedContent } from './RCEWrapper.utils';
65
66
  import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/util/externalToolsForToolbar';
66
67
  import { initScreenreaderOnFormat } from './screenreaderOnFormat';
68
+ import { normalizeContainingContext } from '../util/contextHelper';
67
69
  const RestoreAutoSaveModal = /*#__PURE__*/React.lazy(() => import('./RestoreAutoSaveModal'));
68
70
  const RceHtmlEditor = /*#__PURE__*/React.lazy(() => import('./RceHtmlEditor'));
69
71
  const ASYNC_FOCUS_TIMEOUT = 250;
@@ -141,6 +143,7 @@ class RCEWrapper extends React.Component {
141
143
  this._showOnFocusButton = void 0;
142
144
  this._statusBarId = void 0;
143
145
  this._textareaEl = void 0;
146
+ this._effectiveContainingContext = void 0;
144
147
  this.AIToolsTray = void 0;
145
148
  this.editor = void 0;
146
149
  this.initialContent = void 0;
@@ -208,6 +211,7 @@ class RCEWrapper extends React.Component {
208
211
  this.toggleFullscreen = () => {
209
212
  this.handleClickFullscreen();
210
213
  };
214
+ // @ts-expect-error
211
215
  this._onFullscreenChange = event => {
212
216
  if (document[FS_ELEMENT]) {
213
217
  // @ts-expect-error
@@ -238,6 +242,7 @@ class RCEWrapper extends React.Component {
238
242
  this.handleFocusRCE = () => {
239
243
  this.handleFocus();
240
244
  };
245
+ // @ts-expect-error
241
246
  this.handleBlurRCE = event => {
242
247
  if (event.relatedTarget === null) {
243
248
  // focus might be moving to tinymce
@@ -268,13 +273,11 @@ class RCEWrapper extends React.Component {
268
273
  if (event.code === 'F9' && event.altKey) {
269
274
  event.preventDefault();
270
275
  event.stopPropagation();
271
- this.setFocusAbilityForHeader(true);
272
276
  // @ts-expect-error
273
277
  focusFirstMenuButton(this._elementRef.current);
274
278
  } else if (event.code === 'F10' && event.altKey) {
275
279
  event.preventDefault();
276
280
  event.stopPropagation();
277
- this.setFocusAbilityForHeader(true);
278
281
  // @ts-expect-error
279
282
  focusToolbar(this._elementRef.current);
280
283
  } else if (event.code === 'F8' && event.altKey) {
@@ -329,20 +332,6 @@ class RCEWrapper extends React.Component {
329
332
  tinyapp.setAttribute('tabIndex', '-1');
330
333
  }
331
334
 
332
- // Adds a focusout event listener for handling screen reader navigation focus
333
- const header = this._elementRef.current?.querySelector('.tox-editor-header');
334
- if (header) {
335
- // @ts-expect-error
336
- header.addEventListener('focusout', e => {
337
- // @ts-expect-error
338
- const leavingHeader = !header.contains(e.relatedTarget);
339
- if (leavingHeader) {
340
- this.setFocusAbilityForHeader(false);
341
- }
342
- });
343
- }
344
- this.setFocusAbilityForHeader(false);
345
-
346
335
  // Probably should do this in tinymce.scss, but we only want it in new rce
347
336
  textarea.style.resize = 'none';
348
337
  editor.on('keydown', this.handleKey);
@@ -388,6 +377,12 @@ class RCEWrapper extends React.Component {
388
377
  });
389
378
  }
390
379
  };
380
+ /**
381
+ * Fix keyboard navigation in the expanded toolbar
382
+ *
383
+ * NOTE: This is a workaround for https://github.com/tinymce/tinymce/issues/8618
384
+ * and should be removed once that issue is resolved and the tinymce dependency is updated to include it.
385
+ */
391
386
  this.fixToolbarKeyboardNavigation = () => {
392
387
  // The keyboard navigation config in tinymce for the expanded toolbar is incorrectly configured,
393
388
  // and stops at [data-alloy-tabstop] elements.
@@ -397,6 +392,19 @@ class RCEWrapper extends React.Component {
397
392
  // in https://github.com/tinymce/tinymce/blob/develop/modules/alloy/src/main/ts/ephox/alloy/ui/schema/SplitSlidingToolbarSchema.ts
398
393
  this._elementRef.current?.querySelectorAll('.tox-toolbar-overlord button[data-alloy-tabstop]').forEach(it => it.removeAttribute('data-alloy-tabstop'));
399
394
  };
395
+ /**
396
+ * Sets up selection saving and restoration logic.
397
+ *
398
+ * There are certain actions a user can take when the RCE is not focused that clear the selection inside the
399
+ * editor, such as invoking the Find feature of the browser. If the user then tries to insert content without
400
+ * going back to the editor, the content would be inserted at the top of the RCE, instead of where their cursor
401
+ * was.
402
+ *
403
+ * This method adds logic that saves and restores the selection to work around the issue.
404
+ *
405
+ * @private
406
+ */
407
+ // @ts-expect-error
400
408
  this._setupSelectionSaving = editor => {
401
409
  // @ts-expect-error
402
410
  let savedSelection = null;
@@ -455,6 +463,7 @@ class RCEWrapper extends React.Component {
455
463
  });
456
464
  };
457
465
  this.announcing = 0;
466
+ /* ********** autosave support *************** */
458
467
  this.initAutoSave = editor => {
459
468
  var _this$props$userCache;
460
469
  this.storage = new EncryptedStorage((_this$props$userCache = this.props.userCacheKey) !== null && _this$props$userCache !== void 0 ? _this$props$userCache : '');
@@ -489,6 +498,7 @@ class RCEWrapper extends React.Component {
489
498
  }
490
499
  }
491
500
  };
501
+ // remove any autosaved value that's too old
492
502
  this.cleanupAutoSave = (deleteAll = false) => {
493
503
  if (this.storage) {
494
504
  const expiry = deleteAll ? Date.now() : Date.now() - (this.props.autosave?.maxAge || 0);
@@ -504,6 +514,7 @@ class RCEWrapper extends React.Component {
504
514
  }
505
515
  }
506
516
  };
517
+ // @ts-expect-error
507
518
  this.restoreAutoSave = ans => {
508
519
  this.setState({
509
520
  confirmAutoSave: false
@@ -518,6 +529,7 @@ class RCEWrapper extends React.Component {
518
529
  // let the content be restored
519
530
  debounce(this.checkAccessibility, 1000)();
520
531
  };
532
+ // @ts-expect-error
521
533
  this.doAutoSave = (e, retry = false) => {
522
534
  if (this.storage) {
523
535
  const editor = this.mceInstance();
@@ -542,6 +554,7 @@ class RCEWrapper extends React.Component {
542
554
  }
543
555
  }
544
556
  };
557
+ /* *********** end autosave support *************** */
545
558
  this.onWordCountUpdate = e => {
546
559
  if (!this.editor) return;
547
560
  const shouldIgnore = countShouldIgnore(this.editor, 'body', 'words');
@@ -554,6 +567,7 @@ class RCEWrapper extends React.Component {
554
567
  } else return null;
555
568
  });
556
569
  };
570
+ // @ts-expect-error
557
571
  this.onNodeChange = e => {
558
572
  // This is basically copied out of the tinymce silver theme code for the status bar
559
573
  const path = e.parents.filter(p => p.nodeName !== 'BR' && !p.getAttribute('data-mce-bogus') && p.getAttribute('data-mce-type') !== 'bookmark')
@@ -589,7 +603,9 @@ class RCEWrapper extends React.Component {
589
603
  height: newHeight
590
604
  });
591
605
  // play nice and send the same event that the silver theme would send
592
- editor.fire('ResizeEditor');
606
+ editor.fire('ResizeEditor', {
607
+ deltaY: coordinates.deltaY
608
+ });
593
609
  }
594
610
  };
595
611
  this.onA11yChecker = triggerElementId => {
@@ -705,13 +721,6 @@ class RCEWrapper extends React.Component {
705
721
  content: this.mceInstance().getContent()
706
722
  };
707
723
  };
708
- this.setFocusAbilityForHeader = focusable => {
709
- // Sets aria-hidden to prevent screen readers focus in RCE menus and toolbar
710
- const header = this._elementRef.current?.querySelector('.tox-editor-header');
711
- if (header) {
712
- header.setAttribute('aria-hidden', focusable ? 'false' : 'true');
713
- }
714
- };
715
724
  this.handleTextareaChange = () => {
716
725
  if (this.isHidden()) {
717
726
  this.setCode(this.textareaValue());
@@ -737,6 +746,9 @@ class RCEWrapper extends React.Component {
737
746
  };
738
747
  });
739
748
  };
749
+ /**
750
+ * Used for reseting the value during tests
751
+ */
740
752
  this.resetAlertId = () => {
741
753
  if (this.state.messages.length > 0) {
742
754
  throw new Error('There are messages currently, you cannot reset when they are non-zero');
@@ -828,6 +840,7 @@ class RCEWrapper extends React.Component {
828
840
  this._handleFullscreenResize();
829
841
  });
830
842
  this.AIToolsTray = undefined;
843
+ this._effectiveContainingContext = normalizeContainingContext(this.props.trayProps?.containingContext);
831
844
  }
832
845
 
833
846
  // when the RCE is put into fullscreen we need to move the div
@@ -853,7 +866,6 @@ class RCEWrapper extends React.Component {
853
866
  new_math_equation_handling = false,
854
867
  explicit_latex_typesetting = false,
855
868
  rce_transform_loaded_content = false,
856
- media_links_use_attachment_id = false,
857
869
  rce_find_replace = false,
858
870
  file_verifiers_for_quiz_links = false,
859
871
  consolidated_media_player = false
@@ -862,7 +874,6 @@ class RCEWrapper extends React.Component {
862
874
  new_math_equation_handling,
863
875
  explicit_latex_typesetting,
864
876
  rce_transform_loaded_content,
865
- media_links_use_attachment_id,
866
877
  file_verifiers_for_quiz_links,
867
878
  rce_find_replace,
868
879
  consolidated_media_player
@@ -1119,8 +1130,7 @@ class RCEWrapper extends React.Component {
1119
1130
  if (this.editor) {
1120
1131
  return this.editor;
1121
1132
  }
1122
- const editors = this.props.tinymce.editors || [];
1123
- return editors.filter(ed => ed.id === this.props.textareaId)[0];
1133
+ return this.props.tinymce.get(this.props.textareaId);
1124
1134
  }
1125
1135
 
1126
1136
  // @ts-expect-error
@@ -1195,9 +1205,6 @@ class RCEWrapper extends React.Component {
1195
1205
  document[FS_EXIT]();
1196
1206
  }
1197
1207
  }
1198
-
1199
- // @ts-expect-error
1200
-
1201
1208
  _getStatusBarHeight() {
1202
1209
  // the height prop is the height of the editor and does not include
1203
1210
  // the status bar. we'll need this later.
@@ -1343,9 +1350,6 @@ class RCEWrapper extends React.Component {
1343
1350
  }, ASYNC_FOCUS_TIMEOUT);
1344
1351
  }
1345
1352
  }
1346
-
1347
- // @ts-expect-error
1348
-
1349
1353
  // @ts-expect-error
1350
1354
  call(methodName, ...args) {
1351
1355
  // since exists? has a ? and cant be a regular function just return true
@@ -1356,28 +1360,6 @@ class RCEWrapper extends React.Component {
1356
1360
  // @ts-expect-error
1357
1361
  return this[methodName](...args);
1358
1362
  }
1359
-
1360
- /**
1361
- * Fix keyboard navigation in the expanded toolbar
1362
- *
1363
- * NOTE: This is a workaround for https://github.com/tinymce/tinymce/issues/8618
1364
- * and should be removed once that issue is resolved and the tinymce dependency is updated to include it.
1365
- */
1366
-
1367
- /**
1368
- * Sets up selection saving and restoration logic.
1369
- *
1370
- * There are certain actions a user can take when the RCE is not focused that clear the selection inside the
1371
- * editor, such as invoking the Find feature of the browser. If the user then tries to insert content without
1372
- * going back to the editor, the content would be inserted at the top of the RCE, instead of where their cursor
1373
- * was.
1374
- *
1375
- * This method adds logic that saves and restores the selection to work around the issue.
1376
- *
1377
- * @private
1378
- */
1379
- // @ts-expect-error
1380
-
1381
1363
  announceContextToolbars(editor) {
1382
1364
  editor.on('NodeChange', () => {
1383
1365
  const node = editor.selection.getNode();
@@ -1416,14 +1398,21 @@ class RCEWrapper extends React.Component {
1416
1398
  this.announcing = 0;
1417
1399
  }
1418
1400
  });
1401
+ editor.on('ResizeEditor', ({
1402
+ deltaY
1403
+ }) => {
1404
+ if (!deltaY) return;
1405
+ if (deltaY < 0) {
1406
+ this.setState({
1407
+ announcement: formatMessage('The height of Rich Content Area is decreased.')
1408
+ });
1409
+ } else {
1410
+ this.setState({
1411
+ announcement: formatMessage('The height of Rich Content Area is increased.')
1412
+ });
1413
+ }
1414
+ });
1419
1415
  }
1420
-
1421
- /* ********** autosave support *************** */
1422
-
1423
- // remove any autosaved value that's too old
1424
-
1425
- // @ts-expect-error
1426
-
1427
1416
  getAutoSaved(key) {
1428
1417
  let autosaved = null;
1429
1418
  try {
@@ -1448,17 +1437,9 @@ class RCEWrapper extends React.Component {
1448
1437
  return this.props.autosave?.enabled && editorVisible && document.querySelectorAll('.rce-wrapper').length === 1 && storageAvailable();
1449
1438
  }
1450
1439
  get autoSaveKey() {
1451
- // @ts-expect-error
1452
- const userId = this.props.trayProps?.containingContext.userId;
1440
+ const userId = this._effectiveContainingContext?.userId || '-';
1453
1441
  return `rceautosave:${userId}${window.location.href}:${this.props.textareaId}`;
1454
1442
  }
1455
-
1456
- // @ts-expect-error
1457
-
1458
- /* *********** end autosave support *************** */
1459
-
1460
- // @ts-expect-error
1461
-
1462
1443
  componentWillUnmount() {
1463
1444
  if (this.state.shouldShowEditor) {
1464
1445
  window.clearTimeout(this.blurTimer);
@@ -1679,11 +1660,6 @@ class RCEWrapper extends React.Component {
1679
1660
  this.mceInstance().hide();
1680
1661
  }
1681
1662
  }
1682
-
1683
- /**
1684
- * Used for reseting the value during tests
1685
- */
1686
-
1687
1663
  renderHtmlEditor() {
1688
1664
  // the div keeps the editor from collapsing while the code editor is downloaded
1689
1665
  return /*#__PURE__*/React.createElement(Suspense, {
@@ -1732,7 +1708,12 @@ class RCEWrapper extends React.Component {
1732
1708
  }
1733
1709
  });
1734
1710
  }
1735
- const statusBarFeatures = getStatusBarFeaturesForVariant(this.variant, this.props.ai_text_tools);
1711
+ const statusBarOptions = {
1712
+ aiTextTools: this.props.ai_text_tools,
1713
+ isDesktop: tinymce.Env.deviceType.isDesktop(),
1714
+ a11yResizers: !!this.props.features?.rce_a11y_resize
1715
+ };
1716
+ const statusBarFeatures = getStatusBarFeaturesForVariant(this.variant, statusBarOptions);
1736
1717
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("style", null, this.style.css), /*#__PURE__*/React.createElement(StoreProvider, {
1737
1718
  jwt: this.props.trayProps?.jwt,
1738
1719
  refreshToken: this.props.trayProps?.refreshToken,
@@ -1754,7 +1735,7 @@ class RCEWrapper extends React.Component {
1754
1735
  } : undefined,
1755
1736
  onFocus: this.handleFocusRCE,
1756
1737
  onBlur: this.handleBlurRCE
1757
- }, this.state.shouldShowOnFocusButton && /*#__PURE__*/React.createElement(ShowOnFocusButton, {
1738
+ }, this.state.shouldShowOnFocusButton && tinymce.Env.deviceType.isDesktop() && /*#__PURE__*/React.createElement(ShowOnFocusButton, {
1758
1739
  id: `show-on-focus-btn-${this.id}`,
1759
1740
  onClick: this.openKBShortcutModal,
1760
1741
  margin: "xx-small",
@@ -1820,7 +1801,7 @@ class RCEWrapper extends React.Component {
1820
1801
  disabledPlugins: this.pluginsToExclude,
1821
1802
  features: statusBarFeatures,
1822
1803
  onAI: this.handleAIClick
1823
- }), this.props.trayProps?.containingContext && /*#__PURE__*/React.createElement(CanvasContentTray, Object.assign({
1804
+ }), this._effectiveContainingContext && /*#__PURE__*/React.createElement(CanvasContentTray, Object.assign({
1824
1805
  mountNode: instuiPopupMountNodeFn,
1825
1806
  key: this.id,
1826
1807
  canvasOrigin: this.getCanvasUrl(),
@@ -1829,7 +1810,9 @@ class RCEWrapper extends React.Component {
1829
1810
  onTrayClosing: this.handleContentTrayClosing,
1830
1811
  use_rce_icon_maker: this.props.use_rce_icon_maker
1831
1812
  }, trayProps, {
1813
+ containingContext: this._effectiveContainingContext
1832
1814
  // @ts-expect-error
1815
+ ,
1833
1816
  storeProps: storeProps
1834
1817
  })), /*#__PURE__*/React.createElement(KeyboardShortcutModal, {
1835
1818
  onExited: this.KBShortcutModalExited,
@@ -1858,7 +1841,10 @@ class RCEWrapper extends React.Component {
1858
1841
  open: this.state.confirmAutoSave,
1859
1842
  onNo: () => this.restoreAutoSave(false),
1860
1843
  onYes: () => this.restoreAutoSave(true)
1861
- })) : null, /*#__PURE__*/React.createElement(Alert, {
1844
+ })) : null, this.state.announcement &&
1845
+ /*#__PURE__*/
1846
+ // @ts-expect-error
1847
+ React.createElement(Alert, {
1862
1848
  screenReaderOnly: true,
1863
1849
  liveRegion: this.props.liveRegion
1864
1850
  }, this.state.announcement));
@@ -1869,8 +1855,7 @@ RCEWrapper.propTypes = {
1869
1855
  ai_text_tools: _pt.bool,
1870
1856
  autosave: _pt.shape({
1871
1857
  enabled: _pt.bool,
1872
- maxAge: _pt.number,
1873
- interval: _pt.number
1858
+ maxAge: _pt.number
1874
1859
  }),
1875
1860
  canvasOrigin: _pt.string,
1876
1861
  defaultContent: _pt.string,
@@ -170,6 +170,6 @@ export declare const rceWrapperPropTypes: {
170
170
  maxMruTools: PropTypes.Requireable<number>;
171
171
  }>>;
172
172
  ai_text_tools: PropTypes.Requireable<boolean>;
173
- variant: PropTypes.Requireable<"full" | "lite" | "text-only" | "text-block">;
173
+ variant: PropTypes.Requireable<"full" | "lite" | "text-only" | "text-block" | "block-content-editor">;
174
174
  };
175
175
  export type RCEWrapperProps = PropTypes.InferProps<typeof rceWrapperPropTypes>;
@@ -26,8 +26,10 @@ import React, { Component } from 'react';
26
26
  import { func, node, oneOfType, string } from 'prop-types';
27
27
  import { IconButton } from '@instructure/ui-buttons';
28
28
  const hideStyle = {
29
- position: 'absolute',
30
- left: '-9999px'
29
+ opacity: 0,
30
+ width: 0,
31
+ height: 0,
32
+ overflow: 'hidden'
31
33
  };
32
34
  export default class ShowOnFocusButton extends Component {
33
35
  constructor(...args) {
@@ -28,7 +28,7 @@ import { Badge } from '@instructure/ui-badge';
28
28
  import { InstUISettingsProvider } from '@instructure/emotion';
29
29
  import { Text } from '@instructure/ui-text';
30
30
  import { SVGIcon } from '@instructure/ui-svg-images';
31
- import { IconA11yLine, IconKeyboardShortcutsLine, IconMiniArrowEndLine, IconFullScreenLine, IconExitFullScreenLine } from '@instructure/ui-icons';
31
+ import { IconA11yLine, IconKeyboardShortcutsLine, IconMiniArrowEndLine, IconFullScreenLine, IconExitFullScreenLine, IconAddLine, IconCheckMarkIndeterminateLine } from '@instructure/ui-icons';
32
32
  import formatMessage from '../format-message';
33
33
  import ResizeHandle from './ResizeHandle';
34
34
  import { FS_ENABLED } from '../util/fullscreenHelpers';
@@ -62,7 +62,7 @@ StatusBar.propTypes = {
62
62
  onAI: func
63
63
  };
64
64
  StatusBar.defaultProps = {
65
- a11yBadgeColor: '#0374B5',
65
+ a11yBadgeColor: '#2B7ABC',
66
66
  a11yErrorsCount: 0,
67
67
  disabledPlugins: []
68
68
  };
@@ -80,16 +80,15 @@ function renderPathString({
80
80
  }
81
81
  function emptyTagIcon() {
82
82
  return /*#__PURE__*/React.createElement(SVGIcon, {
83
- viewBox: "0 0 24 24",
84
- fontSize: "24px"
83
+ viewBox: "0 0 1920 1920",
84
+ width: "1em",
85
+ height: "1em"
85
86
  }, /*#__PURE__*/React.createElement("g", {
86
- role: "presentation"
87
- }, /*#__PURE__*/React.createElement("text", {
88
- textAnchor: "middle",
89
- x: "12px",
90
- y: "18px",
91
- fontSize: "16"
92
- }, "</>")));
87
+ role: "presentation",
88
+ transform: "scale(28.7) translate(0, 8)"
89
+ }, /*#__PURE__*/React.createElement("path", {
90
+ d: "M0 29.61L0 25.51L23.71 15.50L23.71 19.87L4.91 27.59L23.71 35.38L23.71 39.75L0 29.61ZM26.46 45.87L36.84 8.86L40.36 8.86L30.00 45.87L26.46 45.87ZM66.80 29.61L43.09 39.75L43.09 35.38L61.87 27.59L43.09 19.87L43.09 15.50L66.80 25.51L66.80 29.61Z"
91
+ })));
93
92
  }
94
93
  function findFocusable(el) {
95
94
  // eslint-disable-next-line react/no-find-dom-node
@@ -287,10 +286,15 @@ export default function StatusBar(props) {
287
286
  title: formatMessage('View word and character counts')
288
287
  }, wordCount)));
289
288
  }
290
- function renderSection3(html_view, fullscreen, resize_handle) {
289
+ function renderSection3({
290
+ html_view,
291
+ fullscreen,
292
+ resize_handle,
293
+ a11y_resize_handlers
294
+ }) {
291
295
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
292
296
  className: css(styles.separator)
293
- }), html_view && renderToggleHtml(), fullscreen && renderFullscreen(), resize_handle && renderResizeHandle());
297
+ }), html_view && renderToggleHtml(), a11y_resize_handlers && renderAccessibleResizeHandle(), fullscreen && renderFullscreen(), resize_handle && renderResizeHandle());
294
298
  }
295
299
  function descMsg() {
296
300
  return preferredHtmlEditor() === RAW_HTML_EDITOR_VIEW ? formatMessage('Shift-O to open the pretty html editor.') : formatMessage('The pretty html editor is not keyboard accessible. Press Shift O to open the raw html editor.');
@@ -351,7 +355,11 @@ export default function StatusBar(props) {
351
355
  screenReaderLabel: fullscreen,
352
356
  withBackground: false,
353
357
  withBorder: false
354
- }, props.rceIsFullscreen ? /*#__PURE__*/React.createElement(IconExitFullScreenLine, null) : /*#__PURE__*/React.createElement(IconFullScreenLine, null));
358
+ }, /*#__PURE__*/React.createElement("div", {
359
+ style: {
360
+ fontSize: '0.9rem'
361
+ }
362
+ }, props.rceIsFullscreen ? /*#__PURE__*/React.createElement(IconExitFullScreenLine, null) : /*#__PURE__*/React.createElement(IconFullScreenLine, null)));
355
363
  }
356
364
  function renderResizeHandle() {
357
365
  if (props.rceIsFullscreen) return null;
@@ -364,10 +372,43 @@ export default function StatusBar(props) {
364
372
  }
365
373
  });
366
374
  }
375
+ function renderAccessibleResizeHandle() {
376
+ if (props.rceIsFullscreen) return null;
377
+ const increaseBtnId = 'rce-resize-increase-btn';
378
+ const decreaseBtnId = 'rce-resize-decrease-btn';
379
+ const handleResize = deltaY => {
380
+ props.onResize(null, {
381
+ deltaY,
382
+ deltaX: 0
383
+ });
384
+ };
385
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(IconButton, {
386
+ "data-btn-id": increaseBtnId,
387
+ "data-testid": increaseBtnId,
388
+ color: "secondary",
389
+ title: formatMessage('Increase Rich Content Area'),
390
+ tabIndex: tabIndexForBtn(increaseBtnId),
391
+ onFocus: () => setFocusedBtnId(increaseBtnId),
392
+ withBackground: false,
393
+ withBorder: false,
394
+ onClick: () => handleResize(5)
395
+ }, /*#__PURE__*/React.createElement(IconAddLine, null)), /*#__PURE__*/React.createElement(IconButton, {
396
+ "data-btn-id": decreaseBtnId,
397
+ "data-testid": decreaseBtnId,
398
+ color: "secondary",
399
+ title: formatMessage('Decrease Rich Content Area'),
400
+ tabIndex: tabIndexForBtn(decreaseBtnId),
401
+ onFocus: () => setFocusedBtnId(decreaseBtnId),
402
+ withBackground: false,
403
+ withBorder: false,
404
+ onClick: () => handleResize(-5)
405
+ }, /*#__PURE__*/React.createElement(IconCheckMarkIndeterminateLine, null)));
406
+ }
367
407
  const flexJustify = isHtmlView() ? 'end' : 'start';
368
408
  const html_view = isFeature('html_view') && isAvailable('instructure_html_view');
369
409
  const fullscreen = isFeature('fullscreen') && isAvailable('instructure_fullscreen');
370
410
  const resize_handle = isFeature('resize_handle');
411
+ const a11y_resize_handlers = isFeature('a11y_resize_handlers');
371
412
  return /*#__PURE__*/React.createElement(InstUISettingsProvider, {
372
413
  theme: {
373
414
  componentOverrides: {
@@ -388,7 +429,12 @@ export default function StatusBar(props) {
388
429
  }, isHtmlView() ? renderHtmlEditorMessage() : renderPath()), /*#__PURE__*/React.createElement(Flex.Item, {
389
430
  role: "toolbar",
390
431
  title: formatMessage('Editor Status Bar')
391
- }, renderIconButtons(), isFeature('word_count') && isAvailable('instructure_wordcount') && renderWordCount(), (html_view || fullscreen || resize_handle) && renderSection3(html_view, fullscreen, resize_handle))));
432
+ }, renderIconButtons(), isFeature('word_count') && isAvailable('instructure_wordcount') && renderWordCount(), (html_view || fullscreen || resize_handle) && renderSection3({
433
+ html_view,
434
+ fullscreen,
435
+ resize_handle,
436
+ a11y_resize_handlers
437
+ }))));
392
438
  }
393
439
  const styles = StyleSheet.create({
394
440
  separator: {
@@ -24,6 +24,12 @@
24
24
  */
25
25
  export class AlertHandler {
26
26
  constructor(alertFunc) {
27
+ /**
28
+ * Calls the registered alertFunc assuming one has been set, otherwise
29
+ * it throws.
30
+ *
31
+ * @memberof AlertHandler
32
+ */
27
33
  this.handleAlert = alert => {
28
34
  if (this.alertFunc == null) {
29
35
  throw new Error('Tried to alert without alertFunc being set first');
@@ -32,12 +38,5 @@ export class AlertHandler {
32
38
  };
33
39
  this.alertFunc = alertFunc;
34
40
  }
35
-
36
- /**
37
- * Calls the registered alertFunc assuming one has been set, otherwise
38
- * it throws.
39
- *
40
- * @memberof AlertHandler
41
- */
42
41
  }
43
42
  export default new AlertHandler();
@@ -112,13 +112,13 @@ tinymce.PluginManager.add('instructure-ui-icons', function (editor) {
112
112
  embed: {
113
113
  src: `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
114
114
  <path fill-rule="evenodd" clip-rule="evenodd" d="M13.7647 5.21417C13.6694 5.21417 13.5773 5.23988 13.482 5.24631C12.8329 3.36281 11.0795 2 9 2C6.53506 2 4.52435 3.91029 4.28294 6.34234C4.09341 6.31127 3.90176 6.28556 3.70588 6.28556C1.66235 6.28556 0 7.96764 0 10.0354C0 12.1032 1.66235 13.7853 3.70588 13.7853L5 13.7853V12.7139L3.70588 12.7139C2.24682 12.7139 1.05882 11.5129 1.05882 10.0354C1.05882 8.55798 2.24682 7.35695 3.70588 7.35695C4.40259 7.35695 5.06012 7.62908 5.55882 8.12192L6.29894 7.35588C6.00565 7.0666 5.66788 6.84483 5.30894 6.66912C5.38941 4.67419 7.00835 3.07139 9 3.07139C11.0435 3.07139 12.7059 4.75347 12.7059 6.82126C12.7059 7.1491 12.6635 7.47266 12.582 7.78658L13.6059 8.06085C13.7107 7.65801 13.7647 7.24124 13.7647 6.82126C13.7647 6.64019 13.7308 6.4677 13.7118 6.29199C13.7298 6.29199 13.7467 6.28556 13.7647 6.28556C15.516 6.28556 16.9412 7.72765 16.9412 9.49973C16.9412 11.2718 15.516 12.7139 13.7647 12.7139L13 12.7139V13.7853L13.7647 13.7853C16.1005 13.7853 18 11.8632 18 9.49973C18 7.13624 16.1005 5.21417 13.7647 5.21417Z" fill="#2B3B46"/>
115
- <path fill-rule="evenodd" clip-rule="evenodd" d="M7.72039 10.6479L8.3603 11.1813L6.75882 13.1025L8.36029 15.0239L7.72038 15.5573L5.6748 13.1024L7.72039 10.6479ZM10.2802 10.6479L12.3258 13.1024L10.2802 15.5573L9.64031 15.0239L11.2418 13.1025L9.6403 11.1813L10.2802 10.6479Z" fill="#2D3B45"/>
115
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M7.72039 10.6479L8.3603 11.1813L6.75882 13.1025L8.36029 15.0239L7.72038 15.5573L5.6748 13.1024L7.72039 10.6479ZM10.2802 10.6479L12.3258 13.1024L10.2802 15.5573L9.64031 15.0239L11.2418 13.1025L9.6403 11.1813L10.2802 10.6479Z" fill="#273540"/>
116
116
  </svg>`
117
117
  },
118
118
  buttons: {
119
119
  src: `
120
120
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18">
121
- <g fill="#2D3B45" clip-path="url(#clip0)">
121
+ <g fill="#273540" clip-path="url(#clip0)">
122
122
  <path fill-rule="evenodd" d="M.999993 11l.000001 6H6.99999v-6H.999993zm-.99999933 7H7.99999v-8H-.00000668l3.5e-7 8zM4 7c1.65685 0 3-1.34315 3-3S5.65685 1 4 1 1 2.34315 1 4s1.34315 3 3 3zm0 1c2.20914 0 4-1.79086 4-4S6.20914 0 4 0-3e-7 1.79086-3e-7 4 1.79086 8 4 8z" clip-rule="evenodd"/>
123
123
  <path d="M12.5 10h1v8h-1v-8z"/>
124
124
  <path d="M17 13.5v1H9v-1h8z"/>
@@ -13,7 +13,7 @@ declare class EquationEditorModal extends React.Component<any, any, any> {
13
13
  executeCommand: (cmd: any, advancedCmd: any) => void;
14
14
  handleModalCancel: () => void;
15
15
  handleModalDone: () => void;
16
- renderMathInAdvancedPreview: import("@instructure/debounce").Debounced;
16
+ renderMathInAdvancedPreview: import("@instructure/debounce").Debounced<() => void>;
17
17
  setPreviewElementContent(): void;
18
18
  toggleAdvanced: () => void;
19
19
  toggleAndUpdatePreference: () => void;
@@ -49,6 +49,9 @@ export default class EquationEditorModal extends Component {
49
49
  };
50
50
  this.previewElement = /*#__PURE__*/React.createRef();
51
51
  this.advancedEditor = /*#__PURE__*/React.createRef();
52
+ // ********* //
53
+ // Callbacks //
54
+ // ********* //
52
55
  this.executeCommand = (cmd, advancedCmd) => {
53
56
  if (this.state.advanced) {
54
57
  const insertionText = advancedCmd || cmd;
@@ -123,6 +126,9 @@ export default class EquationEditorModal extends Component {
123
126
  this.handleFieldRef = node => {
124
127
  this.mathField = node;
125
128
  };
129
+ // ******************* //
130
+ // Rendering functions //
131
+ // ******************* //
126
132
  this.renderFooter = () => {
127
133
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
128
134
  "data-testid": "equation-editor-modal-cancel",
@@ -247,11 +253,6 @@ export default class EquationEditorModal extends Component {
247
253
  const normalizedLatex = latex.replace(/\s+/, '');
248
254
  return containsAdvancedSyntax(normalizedLatex);
249
255
  }
250
-
251
- // ********* //
252
- // Callbacks //
253
- // ********* //
254
-
255
256
  setPreviewElementContent() {
256
257
  if (this.state.workingFormula) {
257
258
  this.renderMathInAdvancedPreview();
@@ -259,11 +260,6 @@ export default class EquationEditorModal extends Component {
259
260
  this.previewElement.current.innerHTML = '';
260
261
  }
261
262
  }
262
-
263
- // ******************* //
264
- // Rendering functions //
265
- // ******************* //
266
-
267
263
  componentDidMount() {
268
264
  this.registerBasicEditorListener();
269
265
  this.setPreviewElementContent();
@@ -1,9 +1,9 @@
1
1
  export default Course;
2
- declare function Course({ dispatch, onChange, onLoading, onLoaded, canvasOrigin }: {
3
- dispatch: any;
4
- onChange: any;
5
- onLoading: any;
6
- onLoaded: any;
2
+ declare function Course({ dispatch, onChange, onLoading, onLoaded, canvasOrigin, }: {
3
+ dispatch?: (() => void) | undefined;
4
+ onChange?: (() => void) | undefined;
5
+ onLoading?: (() => void) | undefined;
6
+ onLoaded?: (() => void) | undefined;
7
7
  canvasOrigin: any;
8
8
  }): React.JSX.Element;
9
9
  declare namespace Course {
@@ -14,16 +14,6 @@ declare namespace Course {
14
14
  let onLoaded: PropTypes.Requireable<(...args: any[]) => any>;
15
15
  let canvasOrigin: PropTypes.Validator<string>;
16
16
  }
17
- namespace defaultProps {
18
- export function dispatch_1(): void;
19
- export { dispatch_1 as dispatch };
20
- export function onChange_1(): void;
21
- export { onChange_1 as onChange };
22
- export function onLoading_1(): void;
23
- export { onLoading_1 as onLoading };
24
- export function onLoaded_1(): void;
25
- export { onLoaded_1 as onLoaded };
26
- }
27
17
  }
28
18
  import React from 'react';
29
19
  import PropTypes from 'prop-types';