@instructure/canvas-rce 5.13.6 → 5.14.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.
package/CHANGELOG.md CHANGED
@@ -5,7 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## 5.13.5 - 2024-09-25
8
+ ## 5.14.0 - 2024-10-18
9
+
10
+ ### Added
11
+
12
+ - New optional media player for upload previews
13
+
14
+ ### Fixed
15
+
16
+ - Keyboard trap when switching to the HTML Editor
17
+
18
+ ### Changed
19
+
20
+ - Upgraded React to 18
21
+
22
+ ## 5.13.6 - 2024-09-25
9
23
 
10
24
  ### Fixed
11
25
 
@@ -22,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22
36
  - Added IDs to multiple objects missing IDs
23
37
  - Add loading spinners to image uploads
24
38
 
25
- ## 5.13.4 - 2024-08-12
39
+ ## 5.13.5 - 2024-08-12
26
40
 
27
41
  ### Fixed
28
42
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2022 - present Instructure, Inc.
2
+ * Copyright (C) 2024 - present Instructure, Inc.
3
3
  *
4
4
  * This file is part of Canvas.
5
5
  *
@@ -15,4 +15,7 @@
15
15
  * You should have received a copy of the GNU Affero General Public License along
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
- module.export = {};
18
+
19
+ module.exports = {
20
+ StudioPlayer: () => null
21
+ };
@@ -272,10 +272,6 @@ class RCEWrapper extends React.Component {
272
272
  this.handleFocus(event);
273
273
  };
274
274
 
275
- this.handleFocusHtmlEditor = event => {
276
- this.handleFocus(event);
277
- };
278
-
279
275
  this.handleBlurEditor = (event, _editor) => {
280
276
  const ifr = this.iframe;
281
277
  ifr && ifr.parentElement.classList.remove('active');
@@ -906,7 +902,8 @@ class RCEWrapper extends React.Component {
906
902
  rce_transform_loaded_content = false,
907
903
  media_links_use_attachment_id = false,
908
904
  rce_find_replace = false,
909
- file_verifiers_for_quiz_links = false
905
+ file_verifiers_for_quiz_links = false,
906
+ consolidated_media_player = false
910
907
  } = this.props.features;
911
908
  return {
912
909
  new_math_equation_handling,
@@ -914,7 +911,8 @@ class RCEWrapper extends React.Component {
914
911
  rce_transform_loaded_content,
915
912
  media_links_use_attachment_id,
916
913
  file_verifiers_for_quiz_links,
917
- rce_find_replace
914
+ rce_find_replace,
915
+ consolidated_media_player
918
916
  };
919
917
  }
920
918
 
@@ -1314,19 +1312,6 @@ class RCEWrapper extends React.Component {
1314
1312
  break;
1315
1313
 
1316
1314
  case PRETTY_HTML_EDITOR_VIEW:
1317
- {
1318
- const cmta = this._elementRef.current.querySelector('.CodeMirror textarea');
1319
-
1320
- if (cmta) {
1321
- cmta.focus();
1322
- } else {
1323
- window.setTimeout(() => {
1324
- var _this$_elementRef$cur3;
1325
-
1326
- (_this$_elementRef$cur3 = this._elementRef.current.querySelector('.CodeMirror textarea')) === null || _this$_elementRef$cur3 === void 0 ? void 0 : _this$_elementRef$cur3.focus();
1327
- }, 200);
1328
- }
1329
- }
1330
1315
  break;
1331
1316
 
1332
1317
  case RAW_HTML_EDITOR_VIEW:
@@ -1400,7 +1385,7 @@ class RCEWrapper extends React.Component {
1400
1385
  // we often need a moment to see if focus comes back
1401
1386
  event && event.persist && event.persist();
1402
1387
  this.blurTimer = window.setTimeout(() => {
1403
- var _this$_elementRef$cur4, _event$focusedEditor, _event$relatedTarget, _event$relatedTarget$;
1388
+ var _this$_elementRef$cur3, _event$focusedEditor, _event$relatedTarget, _event$relatedTarget$;
1404
1389
 
1405
1390
  this.blurTimer = 0;
1406
1391
 
@@ -1410,7 +1395,7 @@ class RCEWrapper extends React.Component {
1410
1395
  return;
1411
1396
  }
1412
1397
 
1413
- if ((_this$_elementRef$cur4 = this._elementRef.current) !== null && _this$_elementRef$cur4 !== void 0 && _this$_elementRef$cur4.contains(document.activeElement)) {
1398
+ if ((_this$_elementRef$cur3 = this._elementRef.current) !== null && _this$_elementRef$cur3 !== void 0 && _this$_elementRef$cur3.contains(document.activeElement)) {
1414
1399
  // focus is still somewhere w/in me
1415
1400
  return;
1416
1401
  }
@@ -1763,21 +1748,14 @@ class RCEWrapper extends React.Component {
1763
1748
  }
1764
1749
 
1765
1750
  setEditorView(view) {
1766
- var _this$_elementRef$cur5;
1767
-
1768
1751
  switch (view) {
1769
- case RAW_HTML_EDITOR_VIEW:
1770
- this.mceInstance().hide();
1771
- break;
1772
-
1773
- case PRETTY_HTML_EDITOR_VIEW:
1774
- this.mceInstance().hide();
1775
- (_this$_elementRef$cur5 = this._elementRef.current.querySelector('.CodeMirror')) === null || _this$_elementRef$cur5 === void 0 ? void 0 : _this$_elementRef$cur5.CodeMirror.setCursor(0, 0);
1776
- break;
1777
-
1778
1752
  case WYSIWYG_VIEW:
1779
1753
  this.setCode(this.textareaValue());
1780
1754
  this.mceInstance().show();
1755
+ break;
1756
+
1757
+ default:
1758
+ this.mceInstance().hide();
1781
1759
  }
1782
1760
  }
1783
1761
 
@@ -1806,8 +1784,7 @@ class RCEWrapper extends React.Component {
1806
1784
  onChange: value => {
1807
1785
  this.getTextarea().value = value;
1808
1786
  this.handleTextareaChange();
1809
- },
1810
- onFocus: this.handleFocusHtmlEditor
1787
+ }
1811
1788
  })));
1812
1789
  }
1813
1790
 
@@ -18,38 +18,12 @@
18
18
  import React, { useCallback, useEffect, useRef, useState } from 'react';
19
19
  import { func, string } from 'prop-types';
20
20
  import formatMessage from '../format-message';
21
- import { CodeEditor } from '@instructure/ui-code-editor';
21
+ import { SourceCodeEditor } from '@instructure/ui-source-code-editor';
22
22
  import beautify from 'js-beautify';
23
- const RceHtmlEditor = /*#__PURE__*/React.forwardRef((_ref, editorRef) => {
24
- let {
25
- onFocus,
26
- ...props
27
- } = _ref;
23
+ const RceHtmlEditor = /*#__PURE__*/React.forwardRef((props, editorRef) => {
28
24
  const [code, setCode] = useState(props.code);
29
25
  const label = formatMessage('html code editor');
30
26
  const [dir, setDir] = useState(getComputedStyle(document.body, null).direction);
31
- const [codeMirrorEditorDiv, setCodeMirrorEditorDiv] = useState(null);
32
- useEffect(() => {
33
- ;
34
-
35
- (async () => {
36
- const p = new Promise(resolve => {
37
- const timerid = setInterval(() => {
38
- // scoping querySelector to the container div makes sure we're targeting this CodeEditor
39
- // The CodeMirror docs (https://codemirror.net/doc/manual.html#styling)
40
- // say this is the element we use to set the editor's height
41
- const editor = editorRef.current.querySelector('.CodeMirror');
42
-
43
- if (editor) {
44
- clearInterval(timerid);
45
- setCodeMirrorEditorDiv(editor);
46
- resolve();
47
- }
48
- }, 60);
49
- });
50
- await p;
51
- })();
52
- }, [editorRef]);
53
27
  useEffect(() => {
54
28
  setCode(beautify.html(props.code)); // eslint-disable-next-line react-hooks/exhaustive-deps
55
29
  }, []);
@@ -78,23 +52,7 @@ const RceHtmlEditor = /*#__PURE__*/React.forwardRef((_ref, editorRef) => {
78
52
 
79
53
  setDir(getComputedStyle(editorRef.current || document.body, null).direction);
80
54
  }, [dir, editorRef]);
81
- useEffect(() => {
82
- if (codeMirrorEditorDiv) {
83
- codeMirrorEditorDiv.CodeMirror.setSize(null, props.height);
84
- codeMirrorEditorDiv.style.margin = '0';
85
- codeMirrorEditorDiv.style.border = '0';
86
- }
87
- }, [codeMirrorEditorDiv, props.height]);
88
- const isFocused = useRef(false); // move cursor to the top of the html code when the editor is focused for the first time
89
-
90
- const handleFocus = useCallback((editor, event) => {
91
- if (!isFocused.current) {
92
- editor.setCursor(0, 0);
93
- isFocused.current = true;
94
- }
95
-
96
- onFocus(event);
97
- }, [onFocus]); // setting height on the container keeps the RCE toolbar from jumping
55
+ const direction = ['ltr', 'rtl'].includes(dir) ? dir : 'ltr'; // setting height on the container keeps the RCE toolbar from jumping
98
56
 
99
57
  return /*#__PURE__*/React.createElement("div", {
100
58
  ref: editorRef,
@@ -104,39 +62,30 @@ const RceHtmlEditor = /*#__PURE__*/React.forwardRef((_ref, editorRef) => {
104
62
  overflow: 'hidden',
105
63
  textAlign: 'start'
106
64
  }
107
- }, /*#__PURE__*/React.createElement(CodeEditor, {
65
+ }, /*#__PURE__*/React.createElement(SourceCodeEditor, {
108
66
  label: label,
109
67
  language: "html",
110
- options: {
111
- lineNumbers: true,
112
- lineWrapping: true,
113
- autofocus: false,
114
- spellcheck: true,
115
- extraKeys: {
116
- Tab: false,
117
- 'Shift-Tab': false
118
- },
119
- screenReaderLabel: label,
120
- direction: dir,
121
- rtlMoveVisually: true
122
- },
68
+ lineNumbers: true,
69
+ lineWrapping: true,
70
+ autofocus: true,
71
+ spellcheck: true,
72
+ direction: direction,
73
+ rtlMoveVisually: true,
74
+ height: props.height,
123
75
  value: code,
124
76
  onChange: value => {
125
77
  setCode(value);
126
78
  props.onChange(value);
127
- },
128
- onFocus: handleFocus
79
+ }
129
80
  }));
130
81
  });
131
82
  RceHtmlEditor.propTypes = {
132
83
  code: string.isRequired,
133
84
  height: string,
134
- onChange: func,
135
- onFocus: func
85
+ onChange: func
136
86
  };
137
87
  RceHtmlEditor.defaultProps = {
138
88
  height: 'auto',
139
- onChange: _value => {},
140
- onFocus: () => {}
89
+ onChange: _value => {}
141
90
  };
142
91
  export default RceHtmlEditor;
@@ -77,7 +77,7 @@ export default class EquationEditorModal extends Component {
77
77
  onModalDismiss,
78
78
  onEquationSubmit
79
79
  } = this.props;
80
- const output = this.state.advanced ? this.state.workingFormula : this.mathField.getValue();
80
+ const output = this.state.advanced ? this.state.workingFormula : this.getMathFiled();
81
81
 
82
82
  if (output) {
83
83
  onEquationSubmit(output);
@@ -99,7 +99,7 @@ export default class EquationEditorModal extends Component {
99
99
  this.toggleAdvanced = () => {
100
100
  this.setState(state => {
101
101
  if (state.advanced) {
102
- this.mathField.setValue(state.workingFormula || '');
102
+ this.setMathField(state.workingFormula || '');
103
103
  return {
104
104
  advanced: false,
105
105
  workingFormula: ''
@@ -107,7 +107,7 @@ export default class EquationEditorModal extends Component {
107
107
  } else {
108
108
  return {
109
109
  advanced: true,
110
- workingFormula: this.mathField.getValue()
110
+ workingFormula: this.getMathFiled()
111
111
  };
112
112
  }
113
113
  });
@@ -281,7 +281,7 @@ export default class EquationEditorModal extends Component {
281
281
  this.registerBasicEditorListener();
282
282
  this.setPreviewElementContent();
283
283
  this.stubMacros();
284
- if (!this.state.advanced) this.mathField.setValue(this.state.workingFormula);
284
+ if (!this.state.advanced) this.setMathField(this.state.workingFormula);
285
285
  this.insertNewRange();
286
286
  }
287
287
 
@@ -303,6 +303,14 @@ export default class EquationEditorModal extends Component {
303
303
  });
304
304
  }
305
305
 
306
+ setMathField(formula) {
307
+ this.mathField.setValue(formula);
308
+ }
309
+
310
+ getMathFiled() {
311
+ return this.mathField.getValue();
312
+ }
313
+
306
314
  }
307
315
  EquationEditorModal.debounceRate = 1000;
308
316
  EquationEditorModal.propTypes = {
@@ -20,6 +20,7 @@ import ReactDOM from 'react-dom';
20
20
  import bridge from '../../../../bridge';
21
21
  import { asAudioElement, findMediaPlayerIframe } from '../../shared/ContentSelection';
22
22
  import AudioOptionsTray from '.';
23
+ import RCEGlobals from '../../../RCEGlobals';
23
24
  export const CONTAINER_ID = 'instructure-audio-options-tray-container';
24
25
  export default class TrayController {
25
26
  constructor() {
@@ -87,14 +88,17 @@ export default class TrayController {
87
88
  }
88
89
 
89
90
  _applyAudioOptions(audioOptions) {
90
- if (!audioOptions.media_object_id || audioOptions.media_object_id === 'undefined') {
91
+ const hasAttachmentId = RCEGlobals.getFeatures().media_links_use_attachment_id && audioOptions.attachment_id;
92
+
93
+ if (!hasAttachmentId && (!audioOptions.media_object_id || audioOptions.media_object_id === 'undefined')) {
91
94
  return;
92
95
  }
93
96
 
94
97
  const container = this._audioContainer;
95
98
  return audioOptions.updateMediaObject({
96
99
  media_object_id: audioOptions.media_object_id,
97
- subtitles: audioOptions.subtitles
100
+ subtitles: audioOptions.subtitles,
101
+ attachment_id: audioOptions.attachment_id
98
102
  }).then(() => container === null || container === void 0 ? void 0 : container.contentWindow.location.reload()).catch(ex => {
99
103
  // eslint-disable-next-line no-console
100
104
  console.error('Failed updating audio captions', ex);
@@ -109,7 +113,7 @@ export default class TrayController {
109
113
  window.addEventListener('message', event => {
110
114
  var _event$data;
111
115
 
112
- if ((event === null || event === void 0 ? void 0 : (_event$data = event.data) === null || _event$data === void 0 ? void 0 : _event$data.subject) === "media_tracks_response") {
116
+ if ((event === null || event === void 0 ? void 0 : (_event$data = event.data) === null || _event$data === void 0 ? void 0 : _event$data.subject) === 'media_tracks_response') {
113
117
  var _event$data2;
114
118
 
115
119
  cb(event === null || event === void 0 ? void 0 : (_event$data2 = event.data) === null || _event$data2 === void 0 ? void 0 : _event$data2.payload);
@@ -51,6 +51,7 @@ export default function AudioOptionsTray(_ref) {
51
51
  onSave({
52
52
  media_object_id: audioOptions.id,
53
53
  subtitles,
54
+ attachment_id: audioOptions.attachmentId,
54
55
  updateMediaObject: contentProps.updateMediaObject
55
56
  });
56
57
  };
@@ -146,12 +146,15 @@ export default class TrayController {
146
146
  // If not, we can't update the MediaObject in the canvas db.
147
147
 
148
148
 
149
- if (videoOptions.media_object_id && videoOptions.media_object_id !== 'undefined' && !videoOptions.editLocked) {
149
+ const hasMediaId = videoOptions.media_object_id && videoOptions.media_object_id !== 'undefined' || data.attachment_id && data.attachment_id !== 'undefined';
150
+
151
+ if (hasMediaId && !videoOptions.editLocked) {
150
152
  videoOptions.updateMediaObject(data).then(_r => {
151
153
  if (this.$videoContainer && videoOptions.displayAs === 'embed') {
152
154
  this.$videoContainer.contentWindow.postMessage({
153
155
  subject: 'reload_media',
154
- media_object_id: videoOptions.media_object_id
156
+ media_object_id: videoOptions.media_object_id,
157
+ attachment_id: data.attachment_id
155
158
  }, bridge.canvasOrigin);
156
159
  }
157
160
  }).catch(ex => {
@@ -185,7 +188,7 @@ export default class TrayController {
185
188
  window.addEventListener('message', event => {
186
189
  var _event$data;
187
190
 
188
- if ((event === null || event === void 0 ? void 0 : (_event$data = event.data) === null || _event$data === void 0 ? void 0 : _event$data.subject) === "media_tracks_response") {
191
+ if ((event === null || event === void 0 ? void 0 : (_event$data = event.data) === null || _event$data === void 0 ? void 0 : _event$data.subject) === 'media_tracks_response') {
189
192
  var _event$data2;
190
193
 
191
194
  cb(event === null || event === void 0 ? void 0 : (_event$data2 = event.data) === null || _event$data2 === void 0 ? void 0 : _event$data2.payload);
@@ -79,27 +79,32 @@ export default function (ed, document) {
79
79
  };
80
80
 
81
81
  const trayProps = Bridge.trayProps.get(ed);
82
- ReactDOM.render( /*#__PURE__*/React.createElement(StoreProvider, trayProps, contentProps => /*#__PURE__*/React.createElement(UploadMedia, {
83
- "data-mce-component": true,
84
- rcsConfig: {
85
- contextType: ed.settings.canvas_rce_user_context.type,
86
- contextId: ed.settings.canvas_rce_user_context.id,
87
- origin: originFromHost(contentProps.host),
88
- headers: headerFor(contentProps.jwt)
89
- },
90
- userLocale: Bridge.userLocale,
91
- mountNode: instuiPopupMountNode,
92
- open: true,
93
- liveRegion: () => document.getElementById('flash_screenreader_holder'),
94
- onStartUpload: fileProps => handleStartUpload(fileProps),
95
- onUploadComplete: (err, data) => handleUpload(err, data, contentProps.mediaUploadComplete, uploadBookmark),
96
- onDismiss: handleDismiss,
97
- tabs: {
98
- record: true,
99
- upload: true
100
- },
101
- uploadMediaTranslations: Bridge.uploadMediaTranslations,
102
- media_links_use_attachment_id: RCEGlobals.getFeatures().media_links_use_attachment_id
103
- })), container);
82
+ ReactDOM.render( /*#__PURE__*/React.createElement(StoreProvider, trayProps, contentProps => {
83
+ var _RCEGlobals$getFeatur;
84
+
85
+ return /*#__PURE__*/React.createElement(UploadMedia, {
86
+ "data-mce-component": true,
87
+ rcsConfig: {
88
+ contextType: ed.settings.canvas_rce_user_context.type,
89
+ contextId: ed.settings.canvas_rce_user_context.id,
90
+ origin: originFromHost(contentProps.host),
91
+ headers: headerFor(contentProps.jwt)
92
+ },
93
+ userLocale: Bridge.userLocale,
94
+ mountNode: instuiPopupMountNode,
95
+ open: true,
96
+ liveRegion: () => document.getElementById('flash_screenreader_holder'),
97
+ onStartUpload: fileProps => handleStartUpload(fileProps),
98
+ onUploadComplete: (err, data) => handleUpload(err, data, contentProps.mediaUploadComplete, uploadBookmark),
99
+ onDismiss: handleDismiss,
100
+ tabs: {
101
+ record: true,
102
+ upload: true
103
+ },
104
+ uploadMediaTranslations: Bridge.uploadMediaTranslations,
105
+ media_links_use_attachment_id: RCEGlobals.getFeatures().media_links_use_attachment_id,
106
+ useStudioPlayer: (_RCEGlobals$getFeatur = RCEGlobals.getFeatures()) === null || _RCEGlobals$getFeatur === void 0 ? void 0 : _RCEGlobals$getFeatur.consolidated_media_player
107
+ });
108
+ }), container);
104
109
  });
105
110
  }
@@ -16,10 +16,12 @@
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  import { fromImageEmbed, fromVideoEmbed } from '../instructure_image/ImageEmbedOptions';
19
- import { isOnlyTextSelected } from '../../contentInsertionUtils';
19
+ import { isOnlyTextSelected } from '../../contentInsertionUtils'; // eslint-disable-next-line import/no-nodejs-modules
20
+
20
21
  import * as url from 'url';
21
22
  import formatMessage from '../../../format-message';
22
23
  import { isStudioEmbeddedMedia } from './StudioLtiSupportUtils';
24
+ import RCEGlobals from '../../RCEGlobals';
23
25
  const FILE_DOWNLOAD_PATH_REGEX = /^\/(courses\/\d+\/)?files\/\d+\/download$/;
24
26
  export const LINK_TYPE = 'link';
25
27
  export const FILE_LINK_TYPE = 'file-link';
@@ -143,6 +145,15 @@ export function asAudioElement($element) {
143
145
  } catch (e) {}
144
146
  }
145
147
 
148
+ if (RCEGlobals.getFeatures().media_links_use_attachment_id) {
149
+ const source = $audioIframe.getAttribute('src');
150
+ const matches = source === null || source === void 0 ? void 0 : source.match(/\/media_attachments_iframe\/(\d+)/);
151
+
152
+ if (matches) {
153
+ audioOptions.attachmentId = matches[1];
154
+ }
155
+ }
156
+
146
157
  return audioOptions;
147
158
  }
148
159
 
@@ -211,7 +222,7 @@ export function isImageEmbed($element) {
211
222
  }
212
223
 
213
224
  function isMediaElement($element, mediaType) {
214
- var _tinymceIframeShim$fi;
225
+ var _tinymceIframeShim$fi, _tinymceIframeShim$ge;
215
226
 
216
227
  // the video is hosted in an iframe, but tinymce
217
228
  // wraps it in a span with swizzled attribute names
@@ -226,8 +237,9 @@ function isMediaElement($element, mediaType) {
226
237
  }
227
238
 
228
239
  const media_obj_id = tinymceIframeShim.getAttribute('data-mce-p-data-media-id');
240
+ const is_media_attachment_iframe = (_tinymceIframeShim$ge = tinymceIframeShim.getAttribute('data-mce-p-src')) === null || _tinymceIframeShim$ge === void 0 ? void 0 : _tinymceIframeShim$ge.includes('media_attachments_iframe');
229
241
 
230
- if (!media_obj_id) {
242
+ if (!media_obj_id && !is_media_attachment_iframe) {
231
243
  return false;
232
244
  }
233
245
 
@@ -775,7 +775,7 @@ const locale = {
775
775
  "message": "Cerca"
776
776
  },
777
777
  "find_and_replace_6e345933": {
778
- "message": "Cerca i substitueix"
778
+ "message": "Cerca i substituir"
779
779
  },
780
780
  "finish_bc343002": {
781
781
  "message": "Finalitza"
@@ -2037,6 +2037,9 @@ const locale = {
2037
2037
  "submit_a3cc6859": {
2038
2038
  "message": "Cuir isteach"
2039
2039
  },
2040
+ "submitting_b90fac62": {
2041
+ "message": "Á chur isteach..."
2042
+ },
2040
2043
  "subscript_59744f96": {
2041
2044
  "message": "Foscript"
2042
2045
  },
@@ -19,6 +19,11 @@
19
19
  // Several components use aphrodite, which tries to manipulate the dom
20
20
  // on a timer which expires after the test completes and the document no longer exists
21
21
  import {StyleSheetTestUtils} from 'aphrodite'
22
+ // eslint-disable-next-line import/no-nodejs-modules
23
+ import {TextDecoder, TextEncoder} from 'util'
24
+
25
+ global.TextEncoder = TextEncoder
26
+ global.TextDecoder = TextDecoder
22
27
 
23
28
  /**
24
29
  * We want to ensure errors and warnings get appropriate eyes. If
@@ -45,6 +50,15 @@ const ignoredErrors = [
45
50
  /You seem to have overlapping act\(\) calls/,
46
51
  /A theme registry has already been initialized/,
47
52
  /Warning: Failed prop type: Invalid prop `color` of value `secondary` supplied to `CondensedButton`, expected one of \["primary","primary-inverse"\]./,
53
+ /ReactDOM.render is no longer supported in React 18/,
54
+ /Warning: Failed %s type: %s%s/,
55
+ /Warning: %s: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.%s/,
56
+ /Warning: `ReactDOMTestUtils.act` is deprecated in favor of `React.act`. Import `act` from `react` instead of `react-dom\/test-utils`. See https:\/\/react.dev\/warnings\/react-dom-test-utils for more info./,
57
+ /Warning: `ReactDOMTestUtils.act` is deprecated in favor of `React.act`. Import `act` from `react` instead of `react-dom\/test-utils`./,
58
+ /Warning: unmountComponentAtNode is deprecated and will be removed in the next major release. Switch to the createRoot API. Learn more: https:\/\/reactjs.org\/link\/switch-to-createroot/,
59
+ /Warning: findDOMNode is deprecated and will be removed in the next major release. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https:\/\/reactjs.org\/link\/strict-mode-find-node/,
60
+ /Warning: %s uses the legacy childContextTypes API which is no longer supported and will be removed in the next major release. Use React.createContext\(\) instead/,
61
+ /Warning: %s uses the legacy contextTypes API which is no longer supported and will be removed in the next major release. Use React.createContext\(\) with static contextType instead./,
48
62
  ]
49
63
  const globalWarn = global.console.warn
50
64
  const ignoredWarnings = [
package/jest.config.js CHANGED
@@ -34,6 +34,7 @@ module.exports = {
34
34
  ],
35
35
  ],
36
36
  setupFilesAfterEnv: [
37
+ '@testing-library/jest-dom',
37
38
  '<rootDir>/jest/jest-setup-framework.js',
38
39
  '<rootDir>/../../jest/stubInstUi.js',
39
40
  ],
@@ -48,6 +49,8 @@ module.exports = {
48
49
  // mock the tinymce-react Editor component
49
50
  '@tinymce/tinymce-react': '<rootDir>/src/rce/__mocks__/tinymceReact.jsx',
50
51
  'crypto-es': '<rootDir>/src/rce/__mocks__/_mockCryptoEs.ts',
52
+ '@instructure/studio-player':
53
+ '<rootDir>/__mocks__/@instructure/studio-player/_mockStudioPlayer.js',
51
54
  },
52
55
 
53
56
  transform: {
@@ -60,9 +63,7 @@ module.exports = {
60
63
  ['@babel/preset-react', {}],
61
64
  ['@babel/preset-typescript', {}],
62
65
  ],
63
- plugins: [
64
- ['@babel/plugin-proposal-decorators', {legacy: true}],
65
- ],
66
+ plugins: [['@babel/plugin-proposal-decorators', {legacy: true}]],
66
67
  },
67
68
  ],
68
69
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/canvas-rce",
3
- "version": "5.13.6",
3
+ "version": "5.14.0",
4
4
  "description": "A component wrapping Canvas's usage of Tinymce",
5
5
  "main": "es/index.js",
6
6
  "owner": "LF",
@@ -76,60 +76,60 @@
76
76
  "instrument": false
77
77
  },
78
78
  "dependencies": {
79
- "@instructure/canvas-theme": "^8",
79
+ "@instructure/canvas-theme": "8.56.4",
80
80
  "@instructure/canvas-media": "*",
81
- "@instructure/debounce": "^8",
82
- "@instructure/emotion": "^8.39",
81
+ "@instructure/debounce": "8.56.4",
82
+ "@instructure/emotion": "8.56.4",
83
83
  "@instructure/k5uploader": "*",
84
84
  "@instructure/media-capture": "^9.0.0",
85
- "@instructure/theme-registry": "^8",
86
- "@instructure/ui-a11y-content": "^8",
87
- "@instructure/ui-a11y-utils": "^8",
88
- "@instructure/ui-alerts": "^8",
89
- "@instructure/ui-avatar": "^8",
90
- "@instructure/ui-badge": "^8",
91
- "@instructure/ui-billboard": "^8",
92
- "@instructure/ui-buttons": "^8",
93
- "@instructure/ui-checkbox": "^8",
94
- "@instructure/ui-code-editor": "^8",
95
- "@instructure/ui-color-utils": "^8",
96
- "@instructure/ui-file-drop": "^8",
97
- "@instructure/ui-flex": "^8",
98
- "@instructure/ui-focusable": "^8",
99
- "@instructure/ui-form-field": "^8",
100
- "@instructure/ui-grid": "^8",
101
- "@instructure/ui-heading": "^8",
102
- "@instructure/ui-icons": "^8",
103
- "@instructure/ui-img": "^8",
104
- "@instructure/ui-link": "^8",
105
- "@instructure/ui-list": "^8",
85
+ "@instructure/theme-registry": "8.56.4",
86
+ "@instructure/ui-a11y-content": "8.56.4",
87
+ "@instructure/ui-a11y-utils": "8.56.4",
88
+ "@instructure/ui-alerts": "8.56.4",
89
+ "@instructure/ui-avatar": "8.56.4",
90
+ "@instructure/ui-badge": "8.56.4",
91
+ "@instructure/ui-billboard": "8.56.4",
92
+ "@instructure/ui-buttons": "8.56.4",
93
+ "@instructure/ui-checkbox": "8.56.4",
94
+ "@instructure/ui-source-code-editor": "8.56.4",
95
+ "@instructure/ui-color-utils": "8.56.4",
96
+ "@instructure/ui-file-drop": "8.56.4",
97
+ "@instructure/ui-flex": "8.56.4",
98
+ "@instructure/ui-focusable": "8.56.4",
99
+ "@instructure/ui-form-field": "8.56.4",
100
+ "@instructure/ui-grid": "8.56.4",
101
+ "@instructure/ui-heading": "8.56.4",
102
+ "@instructure/ui-icons": "8.56.4",
103
+ "@instructure/ui-img": "8.56.4",
104
+ "@instructure/ui-link": "8.56.4",
105
+ "@instructure/ui-list": "8.56.4",
106
106
  "@instructure/ui-media-player": "^9.0.0",
107
- "@instructure/ui-menu": "^8",
108
- "@instructure/ui-modal": "^8",
109
- "@instructure/ui-motion": "^8",
110
- "@instructure/ui-number-input": "^8",
111
- "@instructure/ui-overlays": "^8",
112
- "@instructure/ui-pagination": "^8",
113
- "@instructure/ui-popover": "^8",
114
- "@instructure/ui-radio-input": "^8",
115
- "@instructure/ui-react-utils": "^8",
116
- "@instructure/ui-simple-select": "^8",
117
- "@instructure/ui-spinner": "^8",
118
- "@instructure/ui-svg-images": "^8",
119
- "@instructure/ui-table": "^8",
120
- "@instructure/ui-tabs": "^8",
121
- "@instructure/ui-text-area": "^8",
122
- "@instructure/ui-text-input": "^8",
123
- "@instructure/ui-text": "^8",
124
- "@instructure/ui-themes": "^8",
125
- "@instructure/ui-toggle-details": "^8",
126
- "@instructure/ui-tooltip": "^8",
127
- "@instructure/ui-tray": "^8",
128
- "@instructure/ui-tree-browser": "^8",
129
- "@instructure/ui-truncate-text": "^8",
130
- "@instructure/ui-utils": "^8",
131
- "@instructure/ui-view": "^8",
132
- "@instructure/uid": "^8",
107
+ "@instructure/ui-menu": "8.56.4",
108
+ "@instructure/ui-modal": "8.56.4",
109
+ "@instructure/ui-motion": "8.56.4",
110
+ "@instructure/ui-number-input": "8.56.4",
111
+ "@instructure/ui-overlays": "8.56.4",
112
+ "@instructure/ui-pagination": "8.56.4",
113
+ "@instructure/ui-popover": "8.56.4",
114
+ "@instructure/ui-radio-input": "8.56.4",
115
+ "@instructure/ui-react-utils": "8.56.4",
116
+ "@instructure/ui-simple-select": "8.56.4",
117
+ "@instructure/ui-spinner": "8.56.4",
118
+ "@instructure/ui-svg-images": "8.56.4",
119
+ "@instructure/ui-table": "8.56.4",
120
+ "@instructure/ui-tabs": "8.56.4",
121
+ "@instructure/ui-text-area": "8.56.4",
122
+ "@instructure/ui-text-input": "8.56.4",
123
+ "@instructure/ui-text": "8.56.4",
124
+ "@instructure/ui-themes": "8.56.4",
125
+ "@instructure/ui-toggle-details": "8.56.4",
126
+ "@instructure/ui-tooltip": "8.56.4",
127
+ "@instructure/ui-tray": "8.56.4",
128
+ "@instructure/ui-tree-browser": "8.56.4",
129
+ "@instructure/ui-truncate-text": "8.56.4",
130
+ "@instructure/ui-utils": "8.56.4",
131
+ "@instructure/ui-view": "8.56.4",
132
+ "@instructure/uid": "8.56.4",
133
133
  "@sheerun/mutationobserver-shim": "^0.3.2",
134
134
  "@tinymce/tinymce-react": "~3.8.4",
135
135
  "aphrodite": "^2",
@@ -149,10 +149,10 @@
149
149
  "minimatch": "~3.0.4",
150
150
  "moment-timezone": "^0.5.45",
151
151
  "prop-types": "^15",
152
- "react": "^0.14.8 || ^15.0.0 || ^16",
152
+ "react": "^18",
153
153
  "react-aria-live": "^2",
154
154
  "react-color": "^2.13.4",
155
- "react-dom": "^0.14.8 || ^15.0.0 || ^16",
155
+ "react-dom": "^18",
156
156
  "react-draggable": "^3.3.0",
157
157
  "react-redux": "^5",
158
158
  "react-transition-group": "^1",
@@ -178,6 +178,7 @@
178
178
  "@testing-library/react": "^12",
179
179
  "@testing-library/react-hooks": "^5",
180
180
  "@testing-library/user-event": "^14",
181
+ "@types/testing-library__jest-dom": "^5.0.0",
181
182
  "axe-testcafe": "^3",
182
183
  "babel-loader": "^9.1.3",
183
184
  "babel-plugin-dynamic-import-node": "^2.2.0",
@@ -1,124 +0,0 @@
1
- /*
2
- * Copyright (C) 2023 - present Instructure, Inc.
3
- *
4
- * This file is part of Canvas.
5
- *
6
- * Canvas is free software: you can redistribute it and/or modify it under
7
- * the terms of the GNU Affero General Public License as published by the Free
8
- * Software Foundation, version 3 of the License.
9
- *
10
- * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
- * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
- * details.
14
- *
15
- * You should have received a copy of the GNU Affero General Public License along
16
- * with this program. If not, see <http://www.gnu.org/licenses/>.
17
- */
18
- export default {
19
- lib: {
20
- Base: {},
21
- WordArray: {},
22
- BufferedBlockAlgorithm: {},
23
- Hasher: {},
24
- Cipher: {},
25
- StreamCipher: {},
26
- BlockCipherMode: {},
27
- BlockCipher: {},
28
- CipherParams: {},
29
- SerializableCipher: {},
30
- PasswordBasedCipher: {}
31
- },
32
- x64: {
33
- Word: {},
34
- WordArray: {}
35
- },
36
- enc: {
37
- Hex: {},
38
- Latin1: {},
39
- Utf8: {
40
- parse: () => {}
41
- },
42
- Utf16: {},
43
- Utf16BE: {},
44
- Utf16LE: {},
45
- Base64: {},
46
- Base64url: {}
47
- },
48
- algo: {
49
- HMAC: {},
50
- MD5: {},
51
- SHA1: {},
52
- SHA224: {},
53
- SHA256: {},
54
- SHA384: {},
55
- SHA512: {},
56
- SHA3: {},
57
- RIPEMD160: {},
58
- PBKDF2: {},
59
- EvpKDF: {},
60
- AES: {},
61
- DES: {},
62
- TripleDES: {},
63
- Rabbit: {},
64
- RabbitLegacy: {},
65
- RC4: {},
66
- RC4Drop: {},
67
- Blowfish: {}
68
- },
69
- mode: {
70
- CBC: {},
71
- CFB: {},
72
- CTR: {},
73
- CTRGladman: {},
74
- ECB: {},
75
- OFB: {}
76
- },
77
- pad: {
78
- Pkcs7: {},
79
- AnsiX923: {},
80
- Iso10126: {},
81
- Iso97971: {},
82
- NoPadding: {},
83
- ZeroPadding: {}
84
- },
85
- format: {
86
- OpenSSL: {},
87
- Hex: {}
88
- },
89
- kdf: {
90
- OpenSSL: {}
91
- },
92
- MD5: {},
93
- HmacMD5: {},
94
- SHA1: {},
95
- HmacSHA1: {},
96
- SHA224: {},
97
- HmacSHA224: {},
98
- SHA256: {},
99
- HmacSHA256: {},
100
- SHA384: {},
101
- HmacSHA384: {},
102
- SHA512: {},
103
- HmacSHA512: {},
104
- SHA3: {},
105
- HmacSHA3: {},
106
- RIPEMD160: {},
107
- HmacRIPEMD160: {},
108
- PBKDF2: {},
109
- EvpKDF: {},
110
- AES: {},
111
- DES: {},
112
- TripleDES: {},
113
- Rabbit: {},
114
- RabbitLegacy: {},
115
- RC4: {
116
- encrypt: () => {},
117
- decrypt: () => {}
118
- },
119
- RC4Drop: {
120
- encrypt: () => {},
121
- decrypt: () => {}
122
- },
123
- Blowfish: {}
124
- };
@@ -1,55 +0,0 @@
1
- /*
2
- * Copyright (C) 2021 - present Instructure, Inc.
3
- *
4
- * This file is part of Canvas.
5
- *
6
- * Canvas is free software: you can redistribute it and/or modify it under
7
- * the terms of the GNU Affero General Public License as published by the Free
8
- * Software Foundation, version 3 of the License.
9
- *
10
- * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
- * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
- * details.
14
- *
15
- * You should have received a copy of the GNU Affero General Public License along
16
- * with this program. If not, see <http://www.gnu.org/licenses/>.
17
- */
18
-
19
- /*
20
- * This is a mock for the @tinymce/tinymce-react Editor component
21
- * and the inner tinymce editor object
22
- * jest.config.js moduleNameMapper has jest load this
23
- * file in response to
24
- * import {Editor} from '@tinymce/tinymce-react'
25
- * in RCEWrapper.js
26
- */
27
- import React, { useEffect, useRef } from 'react';
28
- import FakeEditor from '../__tests__/FakeEditor';
29
- export function Editor(props) {
30
- const editorRef = useRef(null);
31
- const textareaRef = useRef(null);
32
- const tinymceEditor = useRef(new FakeEditor(props));
33
- window.tinymce.editors[0] = tinymceEditor.current;
34
- useEffect(() => {
35
- tinymceEditor.current.on('change', handleChange);
36
- props.onInit && props.onInit({}, tinymceEditor.current); // eslint-disable-next-line react-hooks/exhaustive-deps
37
- }, []);
38
-
39
- function handleChange(event) {
40
- var _props$onEditorChange;
41
-
42
- (_props$onEditorChange = props.onEditorChange) === null || _props$onEditorChange === void 0 ? void 0 : _props$onEditorChange.call(props, event.target.value);
43
- }
44
-
45
- return /*#__PURE__*/React.createElement("div", {
46
- ref: editorRef
47
- }, /*#__PURE__*/React.createElement("textarea", {
48
- ref: textareaRef,
49
- id: props.id,
50
- name: props.textareaName,
51
- value: props.initialValue,
52
- onInput: handleChange,
53
- onChange: handleChange
54
- }));
55
- }
@@ -1,24 +0,0 @@
1
- /*
2
- * Copyright (C) 2019 - present Instructure, Inc.
3
- *
4
- * This file is part of Canvas.
5
- *
6
- * Canvas is free software: you can redistribute it and/or modify it under
7
- * the terms of the GNU Affero General Public License as published by the Free
8
- * Software Foundation, version 3 of the License.
9
- *
10
- * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
- * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
- * details.
14
- *
15
- * You should have received a copy of the GNU Affero General Public License along
16
- * with this program. If not, see <http://www.gnu.org/licenses/>.
17
- */
18
- const screenfull = {
19
- on() {},
20
-
21
- off() {}
22
-
23
- };
24
- export default screenfull;
@@ -1,53 +0,0 @@
1
- /*
2
- * Copyright (C) 2023 - present Instructure, Inc.
3
- *
4
- * This file is part of Canvas.
5
- *
6
- * Canvas is free software: you can redistribute it and/or modify it under
7
- * the terms of the GNU Affero General Public License as published by the Free
8
- * Software Foundation, version 3 of the License.
9
- *
10
- * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
- * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
- * details.
14
- *
15
- * You should have received a copy of the GNU Affero General Public License along
16
- * with this program. If not, see <http://www.gnu.org/licenses/>.
17
- */
18
- export default [{
19
- test: jest.fn().mockReturnValue(false),
20
- data: jest.fn().mockReturnValue({
21
- select: 'a',
22
- checkbox: true,
23
- color: 'rgba(40, 100, 200, 0.6)',
24
- text: 'Text'
25
- }),
26
- form: jest.fn().mockReturnValue([{
27
- label: 'Select Field',
28
- dataKey: 'select',
29
- options: [['a', 'A'], ['b', 'B']]
30
- }, {
31
- label: 'Select Field',
32
- dataKey: 'checkbox',
33
- checkbox: true
34
- }, {
35
- label: 'Select Field',
36
- dataKey: 'color',
37
- color: true
38
- }, {
39
- label: 'Text Field',
40
- dataKey: 'text',
41
- disabledIf: () => true
42
- }, {
43
- label: 'Text Area',
44
- dataKey: 'textarea',
45
- textarea: true
46
- }]),
47
- rootNode: jest.fn(),
48
- update: jest.fn(),
49
- message: jest.fn().mockReturnValue('Error Message'),
50
- why: jest.fn().mockReturnValue('Why Text'),
51
- link: 'http://some-url',
52
- linkText: jest.fn().mockReturnValue('Link for learning more')
53
- }];