@instructure/canvas-rce 5.15.6 → 6.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 (77) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/Dockerfile +1 -1
  3. package/es/bridge/Bridge.d.ts +6 -0
  4. package/es/defaultTinymceConfig.js +165 -4
  5. package/es/enhance-user-content/enhance_user_content.js +1 -1
  6. package/es/enhance-user-content/instructure_helper.js +7 -3
  7. package/es/rce/RCEWrapper.js +3 -0
  8. package/es/rce/contentRendering.js +3 -2
  9. package/es/rce/plugins/instructure_image/ImageOptionsTray/index.js +33 -9
  10. package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.d.ts +1 -1
  11. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +13 -3
  12. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +10 -0
  13. package/es/rce/plugins/instructure_record/mediaTranslations.d.ts +3 -0
  14. package/es/rce/plugins/instructure_record/mediaTranslations.js +4 -1
  15. package/es/rce/plugins/shared/DimensionsInput/DimensionInput.d.ts +4 -2
  16. package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +10 -3
  17. package/es/rce/plugins/shared/DimensionsInput/index.d.ts +2 -0
  18. package/es/rce/plugins/shared/DimensionsInput/index.js +9 -5
  19. package/es/rce/plugins/shared/ImageOptionsForm.d.ts +4 -1
  20. package/es/rce/plugins/shared/ImageOptionsForm.js +13 -3
  21. package/es/rce/plugins/shared/Upload/UrlPanel.d.ts +9 -3
  22. package/es/rce/plugins/shared/Upload/UrlPanel.js +13 -4
  23. package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +1 -1
  24. package/es/rce/plugins/tinymce-a11y-checker/node-checker.js +3 -2
  25. package/es/rce/plugins/tinymce-a11y-checker/utils/dom.d.ts +6 -0
  26. package/es/rce/plugins/tinymce-a11y-checker/utils/dom.js +15 -0
  27. package/es/rce/plugins/tinymce-a11y-checker/utils/rule-enhancer.d.ts +14 -0
  28. package/es/rce/plugins/tinymce-a11y-checker/utils/rule-enhancer.js +53 -0
  29. package/es/rce/screenreaderOnFormat.d.ts +2 -0
  30. package/es/rce/screenreaderOnFormat.js +109 -0
  31. package/es/translations/locales/ar.js +42 -0
  32. package/es/translations/locales/ca.js +42 -0
  33. package/es/translations/locales/cy.js +42 -0
  34. package/es/translations/locales/da-x-k12.js +42 -0
  35. package/es/translations/locales/da.js +42 -0
  36. package/es/translations/locales/de.js +42 -0
  37. package/es/translations/locales/en-AU-x-unimelb.js +42 -0
  38. package/es/translations/locales/en-GB-x-ukhe.js +42 -0
  39. package/es/translations/locales/en.js +51 -0
  40. package/es/translations/locales/en_AU.js +42 -0
  41. package/es/translations/locales/en_CA.js +42 -0
  42. package/es/translations/locales/en_CY.js +42 -0
  43. package/es/translations/locales/en_GB.js +42 -0
  44. package/es/translations/locales/es.js +42 -0
  45. package/es/translations/locales/es_ES.js +42 -0
  46. package/es/translations/locales/fi.js +42 -0
  47. package/es/translations/locales/fr.js +42 -0
  48. package/es/translations/locales/fr_CA.js +42 -0
  49. package/es/translations/locales/ga.js +114 -0
  50. package/es/translations/locales/hi.js +42 -0
  51. package/es/translations/locales/ht.js +42 -0
  52. package/es/translations/locales/id.js +42 -0
  53. package/es/translations/locales/is.js +42 -0
  54. package/es/translations/locales/it.js +42 -0
  55. package/es/translations/locales/ja.js +42 -0
  56. package/es/translations/locales/mi.js +42 -0
  57. package/es/translations/locales/ms.js +42 -0
  58. package/es/translations/locales/nb-x-k12.js +42 -0
  59. package/es/translations/locales/nb.js +42 -0
  60. package/es/translations/locales/nl.js +42 -0
  61. package/es/translations/locales/pl.js +42 -0
  62. package/es/translations/locales/pt.js +42 -0
  63. package/es/translations/locales/pt_BR.js +42 -0
  64. package/es/translations/locales/ru.js +42 -0
  65. package/es/translations/locales/sl.js +42 -0
  66. package/es/translations/locales/sv-x-k12.js +42 -0
  67. package/es/translations/locales/sv.js +42 -0
  68. package/es/translations/locales/th.js +42 -0
  69. package/es/translations/locales/vi.js +42 -0
  70. package/es/translations/locales/zh-Hans.js +42 -0
  71. package/es/translations/locales/zh-Hant.js +42 -0
  72. package/es/translations/locales/zh.js +42 -0
  73. package/es/translations/locales/zh_HK.js +42 -0
  74. package/es/util/loadingPlaceholder.js +4 -3
  75. package/package.json +6 -5
  76. package/coverage/canvas-rce-jest.xml +0 -7028
  77. package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ 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
+ ## 6.0.0 - 2025-03-20
9
+
10
+ ### Changed
11
+
12
+ - Upgraded to Node 20 LTS
13
+
14
+ ### Fixed
15
+
16
+ - Redirect focus on invalid save in Image Options tray
17
+ - Whitelist the aria-description attribute
18
+ - Flag external links when they have multi-part TLDs
19
+ - Save changes to alt text when it is the only thing that changes
20
+ - Screenreader reads out content inside raw HTML editor
21
+ - Validations in Upload Media modal
22
+ - Increased Link header size
23
+
24
+ ## 5.15.8 - 2025-02-20
25
+
26
+ ### Fixed
27
+
28
+ - Fixed invalid `querySelectorAll` selector (`:not(.not_external, .external)`)
29
+ that caused errors in older Chrome versions (87 and below). Updated to
30
+ `:not(.not_external):not(.external)` for improved browser compatibility
31
+ - Improved external link handling logic in canvas-rce
32
+
33
+ ### Added
34
+
35
+ - Jest test to ensure the fix does not introduce regressions
36
+
8
37
  ## 5.15.0 - 2025-02-12
9
38
 
10
39
  - Lazy load iframe and images by default
package/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM instructure/node:18
1
+ FROM instructure/node:20
2
2
 
3
3
  ARG NPM_PRIVATE_SCOPE
4
4
  ARG NPM_PRIVATE_REGISTRY
@@ -31,6 +31,9 @@ export default class Bridge {
31
31
  ADDED_CAPTION: string;
32
32
  DELETED_CAPTION: string;
33
33
  PROGRESS_LABEL: string;
34
+ SELECT_SUPPORTED_FILE_TYPE: string;
35
+ CHOOSE_FILE_TO_UPLOAD: string;
36
+ ENTER_FILE_NAME: string;
34
37
  };
35
38
  SelectStrings: {
36
39
  USE_ARROWS: string;
@@ -113,6 +116,9 @@ export default class Bridge {
113
116
  ADDED_CAPTION: string;
114
117
  DELETED_CAPTION: string;
115
118
  PROGRESS_LABEL: string;
119
+ SELECT_SUPPORTED_FILE_TYPE: string;
120
+ CHOOSE_FILE_TO_UPLOAD: string;
121
+ ENTER_FILE_NAME: string;
116
122
  };
117
123
  SelectStrings: {
118
124
  USE_ARROWS: string;
@@ -16,6 +16,23 @@
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
+ function elemsToTinyStringConfig(list) {
20
+ return Object.entries(list).map(pair => {
21
+ const [tag, attrs] = pair;
22
+ if (attrs.length === 0) {
23
+ return tag;
24
+ } else {
25
+ const attrStr = attrs.map(attr => {
26
+ if (typeof attr === 'string') {
27
+ return attr;
28
+ } else if (typeof attr === 'object') {
29
+ return Object.entries(attr).map(([key, value]) => `${key}=${value}`);
30
+ }
31
+ }).join('|');
32
+ return `${tag}[${attrStr}]`;
33
+ }
34
+ }).join();
35
+ }
19
36
  const defaultTinymceConfig = {
20
37
  // ============================================================================
21
38
  // Values in this section have acceptable defaults which you may want
@@ -73,10 +90,154 @@ const defaultTinymceConfig = {
73
90
  // then edited.
74
91
  //
75
92
  // this list needs to be kept in sync with the list in gems/canvas_sanitize/lib/canvas_sanitize/canvas_sanitize.rb
76
- valid_elements: '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|role],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|data-old-link],strong/b,em/i,strike/s,u,#p,-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt|title|hspace|vspace|width|height|align|role|data-old-link],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value|_value],embed[type|width|height|src|*],map[name],area[shape|coords|href|alt|target],bdo,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,kbd,q[cite],samp,small,tt,var,big,figure,figcaption,source[media|width|height|sizes|src|srcset|type|data-old-link],track,mark,article,aside,details,footer,header,nav,section,summary,time',
77
- extended_valid_elements: '@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|contextmenu|draggable|dropzone|hidden|longdesc|spellcheck|translate|align|role|aria-labelledby|aria-atomic|aria-busy|aria-controls|aria-describedby|aria-disabled|aria-dropeffect|aria-flowto|aria-grabbed|aria-haspopup|aria-hidden|aria-invalid|aria-label|aria-labelledby|aria-live|aria-owns|aria-relevant|aria-autocomplete|aria-checked|aria-disabled|aria-expanded|aria-haspopup|aria-hidden|aria-invalid|aria-label|aria-level|aria-multiline|aria-multiselectable|aria-orientation|aria-pressed|aria-readonly|aria-required|aria-selected|aria-sort|aria-valuemax|aria-valuemin|aria-valuenow|aria-valuetext],iframe[id|data-media-type|title|src|width|height|name|align|style|class|sandbox|loading|allowfullscreen|webkitallowfullscreen|mozallowfullscreen|allow|data-old-link],i[iclass],a[hidden|href|target|rel|media|hreflang|type|charset|name|rev|shape|coords|download|alt],#p,li[value],-ol[reversed|start|type|compact],pre[width],table[border|summary|width|frame|rules|cellspacing|cellpadding|bgcolor],tbody[char|charoff|valign],td[colspan|rowspan|headers|abbr|axis|scope|align|char|charoff|valign|nowrap|bgcolor|width|height],tfoot[char|charoff|valign],th[colspan|rowspan|headers|scope|abbr|axis|align|char|charoff|valign|nowrap|bgcolor|width|height],thead[char|charoff|valign],tr[char|charoff|valign|bgcolor],-ul[compact],video[name|src|allowfullscreen|muted|poster|width|height|controls|playsinline],audio[name|src|muted|controls],annotation[href|xref|definitionURL|encoding|cd|name|src],annotation-xml[href|xref|definitionURL|encoding|cd|name|src],maction[href|xref|mathcolor|mathbackground|actiontype|selection],maligngroup[href|xref|mathcolor|mathbackground|groupalign],malignmark[href|xref|mathcolor|mathbackground|edge],math[xmlns|href|xref|display|maxwidth|overflow|altimg|altimg-width|altimg-height|altimg-valign|alttext|cdgroup|mathcolor|mathbackground|scriptlevel|displaystyle|scriptsizemultiplier|scriptminsize|infixlinebreakstyle|decimalpoint|mathvariant|mathsize|width|height|valign|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast|depth|lquote|rquote|linethickness|munalign|denomalign|bevelled|voffset|open|close|separators|notation|subscriptshift|superscriptshift|accentunder|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|side|minlabelspacing|rowspan|columnspan|edge|stackalign|charalign|charspacing|longdivstyle|position|shift|location|crossout|length|leftoverhang|rightoverhang|mslinethickness|selection],menclose[href|xref|mathcolor|mathbackground|notation],merror[href|xref|mathcolor|mathbackground],mfenced[href|xref|mathcolor|mathbackground|open|close|separators],mfrac[href|xref|mathcolor|mathbackground|linethickness|munalign|denomalign|bevelled],mglyph[href|xref|mathcolor|mathbackground|src|alt|width|height|valign],mi[href|xref|mathcolor|mathbackground|mathvariant|mathsize],mlabeledtr[href|xref|mathcolor|mathbackground],mlongdiv[href|xref|mathcolor|mathbackground|longdivstyle|align|stackalign|charalign|charspacing],mmultiscripts[href|xref|mathcolor|mathbackground|subscriptshift|superscriptshift],mn[href|xref|mathcolor|mathbackground|mathvariant|mathsize],mo[href|xref|mathcolor|mathbackground|mathvariant|mathsize|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast],mover[href|xref|mathcolor|mathbackground|accent|align],mpadded[href|xref|mathcolor|mathbackground|height|depth|width|lspace|voffset],mphantom[href|xref|mathcolor|mathbackground],mprescripts[href|xref|mathcolor|mathbackground],mroot[href|xref|mathcolor|mathbackground],mrow[href|xref|mathcolor|mathbackground],ms[href|xref|mathcolor|mathbackground|mathvariant|mathsize|lquote|rquote],mscarries[href|xref|mathcolor|mathbackground|position|location|crossout|scriptsizemultiplier],mscarry[href|xref|mathcolor|mathbackground|location|crossout],msgroup[href|xref|mathcolor|mathbackground|position|shift],msline[href|xref|mathcolor|mathbackground|position|length|leftoverhang|rightoverhang|mslinethickness],mspace[href|xref|mathcolor|mathbackground|mathvariant|mathsize],msqrt[href|xref|mathcolor|mathbackground],msrow[href|xref|mathcolor|mathbackground|position],mstack[href|xref|mathcolor|mathbackground|align|stackalign|charalign|charspacing],mstyle[href|xref|mathcolor|mathbackground|scriptlevel|displaystyle|scriptsizemultiplier|scriptminsize|infixlinebreakstyle|decimalpoint|mathvariant|mathsize|width|height|valign|form|fence|separator|lspace|rspace|stretchy|symmetric|maxsize|minsize|largeop|movablelimits|accent|linebreak|lineleading|linebreakstyle|linebreakmultchar|indentalign|indentshift|indenttarget|indentalignfirst|indentshiftfirst|indentalignlast|indentshiftlast|depth|lquote|rquote|linethickness|munalign|denomalign|bevelled|voffset|open|close|separators|notation|subscriptshift|superscriptshift|accentunder|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|side|minlabelspacing|rowspan|columnspan|edge|stackalign|charalign|charspacing|longdivstyle|position|shift|location|crossout|length|leftoverhang|rightoverhang|mslinethickness|selection],msub[href|xref|mathcolor|mathbackground|subscriptshift],msubsup[href|xref|mathcolor|mathbackground|subscriptshift|superscriptshift],msup[href|xref|mathcolor|mathbackground|superscriptshift],mtable[href|xref|mathcolor|mathbackground|align|rowalign|columnalign|groupalign|alignmentscope|columnwidth|width|rowspacing|columnspacing|rowlines|columnlines|frame|framespacing|equalrows|equalcolumns|displaystyle|side|minlabelspacing],mtd[href|xref|mathcolor|mathbackground|rowspan|columnspan|rowalign|columnalign|groupalign],mtext[href|xref|mathcolor|mathbackground|mathvariant|mathsize|width|height|depth|linebreak],mtr[href|xref|mathcolor|mathbackground|rowalign|columnalign|groupalign],munder[href|xref|mathcolor|mathbackground|accentunder|align],munderover[href|xref|mathcolor|mathbackground|accent|accentunder|align],none[href|xref|mathcolor|mathbackground],semantics[href|xref|definitionURL|encoding],picture,ruby,rp,rt,wbr' +
78
- // the svg necessary for the uploading placeholder's spinner
79
- 'svg[*],g[*],circle[*]',
93
+ valid_elements: elemsToTinyStringConfig({
94
+ '@': ['id', 'class', 'style', 'title', 'dir<ltr?rtl', 'lang', 'xml::lang', 'role'],
95
+ 'a': ['rel', 'rev', 'charset', 'hreflang', 'tabindex', 'accesskey', 'type', 'name', 'href', 'target', 'title', 'class', 'data-old-link'],
96
+ 'strong/b': [],
97
+ 'em/i': [],
98
+ 'strike/s': [],
99
+ 'u': [],
100
+ '#p': [],
101
+ '-ol': ['type', 'compact'],
102
+ '-ul': ['type', 'compact'],
103
+ '-li': [],
104
+ 'br': [],
105
+ 'img': ['longdesc', 'usemap', 'src', 'border', 'alt', 'title', 'hspace', 'vspace', 'width', 'height', 'align', 'role', 'data-old-link'],
106
+ '-sub': [],
107
+ '-sup': [],
108
+ '-blockquote': ['cite'],
109
+ '-table': [{
110
+ 'border': '0'
111
+ }, 'cellspacing', 'cellpadding', 'width', 'frame', 'rules', 'height', 'align', 'summary', 'bgcolor', 'background', 'bordercolor'],
112
+ '-tr': ['rowspan', 'width', 'height', 'align', 'valign', 'bgcolor', 'background', 'bordercolor'],
113
+ 'tbody': [],
114
+ 'thead': [],
115
+ 'tfoot': [],
116
+ '#td': ['colspan', 'rowspan', 'width', 'height', 'align', 'valign', 'bgcolor', 'background', 'bordercolor', 'scope'],
117
+ '#th': ['colspan', 'rowspan', 'width', 'height', 'align', 'valign', 'scope'],
118
+ 'caption': [],
119
+ '-div': [],
120
+ '-span': [],
121
+ '-code': [],
122
+ '-pre': [],
123
+ 'address': [],
124
+ '-h1': [],
125
+ '-h2': [],
126
+ '-h3': [],
127
+ '-h4': [],
128
+ '-h5': [],
129
+ '-h6': [],
130
+ 'hr': ['size', 'noshade'],
131
+ '-font': ['face', 'size', 'color'],
132
+ 'dd': [],
133
+ 'dl': [],
134
+ 'dt': [],
135
+ 'cite': [],
136
+ 'abbr': [],
137
+ 'acronym': [],
138
+ 'del': ['datetime', 'cite'],
139
+ 'ins': ['datetime', 'cite'],
140
+ 'object': ['classid', 'width', 'height', 'codebase', '*'],
141
+ 'param': ['name', 'value', '_value'],
142
+ 'embed': ['type', 'width', 'height', 'src', '*'],
143
+ 'map': ['name'],
144
+ 'area': ['shape', 'coords', 'href', 'alt', 'target'],
145
+ 'bdo': [],
146
+ 'col': ['align', 'char', 'charoff', 'span', 'valign', 'width'],
147
+ 'colgroup': ['align', 'char', 'charoff', 'span', 'valign', 'width'],
148
+ 'dfn': [],
149
+ 'kbd': [],
150
+ 'q': ['cite'],
151
+ 'samp': [],
152
+ 'small': [],
153
+ 'tt': [],
154
+ 'var': [],
155
+ 'big': [],
156
+ 'figure': [],
157
+ 'figcaption': [],
158
+ 'source': ['media', 'width', 'height', 'sizes', 'src', 'srcset', 'type', 'data-old-link'],
159
+ 'track': [],
160
+ 'mark': [],
161
+ 'article': [],
162
+ 'aside': [],
163
+ 'details': [],
164
+ 'footer': [],
165
+ 'header': [],
166
+ 'nav': [],
167
+ 'section': [],
168
+ 'summary': [],
169
+ 'time': []
170
+ }),
171
+ extended_valid_elements: elemsToTinyStringConfig({
172
+ '@': ['id', 'accesskey', 'class', 'dir', 'lang', 'style', 'tabindex', 'title', 'contenteditable', 'contextmenu', 'draggable', 'dropzone', 'hidden', 'longdesc', 'spellcheck', 'translate', 'align', 'role', 'aria-labelledby', 'aria-atomic', 'aria-busy', 'aria-controls', 'aria-describedby', 'aria-description', 'aria-disabled', 'aria-dropeffect', 'aria-flowto', 'aria-grabbed', 'aria-haspopup', 'aria-hidden', 'aria-invalid', 'aria-label', 'aria-labelledby', 'aria-live', 'aria-owns', 'aria-relevant', 'aria-autocomplete', 'aria-checked', 'aria-disabled', 'aria-expanded', 'aria-haspopup', 'aria-hidden', 'aria-invalid', 'aria-label', 'aria-level', 'aria-multiline', 'aria-multiselectable', 'aria-orientation', 'aria-pressed', 'aria-readonly', 'aria-required', 'aria-selected', 'aria-sort', 'aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext'],
173
+ 'iframe': ['id', 'data-media-type', 'title', 'src', 'width', 'height', 'name', 'align', 'style', 'class', 'sandbox', 'loading', 'allowfullscreen', 'webkitallowfullscreen', 'mozallowfullscreen', 'allow', 'data-old-link'],
174
+ 'i': ['iclass'],
175
+ 'a': ['hidden', 'href', 'target', 'rel', 'media', 'hreflang', 'type', 'charset', 'name', 'rev', 'shape', 'coords', 'download', 'alt'],
176
+ '#p': [],
177
+ 'li': ['value'],
178
+ '-ol': ['reversed', 'start', 'type', 'compact'],
179
+ 'pre': ['width'],
180
+ 'table': ['border', 'summary', 'width', 'frame', 'rules', 'cellspacing', 'cellpadding', 'bgcolor'],
181
+ 'tbody': ['char', 'charoff', 'valign'],
182
+ 'td': ['colspan', 'rowspan', 'headers', 'abbr', 'axis', 'scope', 'align', 'char', 'charoff', 'valign', 'nowrap', 'bgcolor', 'width', 'height'],
183
+ 'tfoot': ['char', 'charoff', 'valign'],
184
+ 'th': ['colspan', 'rowspan', 'headers', 'scope', 'abbr', 'axis', 'align', 'char', 'charoff', 'valign', 'nowrap', 'bgcolor', 'width', 'height'],
185
+ 'thead': ['char', 'charoff', 'valign'],
186
+ 'tr': ['char', 'charoff', 'valign', 'bgcolor'],
187
+ '-ul': ['compact'],
188
+ 'video': ['name', 'src', 'allowfullscreen', 'muted', 'poster', 'width', 'height', 'controls', 'playsinline'],
189
+ 'audio': ['name', 'src', 'muted', 'controls'],
190
+ 'annotation': ['href', 'xref', 'definitionURL', 'encoding', 'cd', 'name', 'src'],
191
+ 'annotation-xml': ['href', 'xref', 'definitionURL', 'encoding', 'cd', 'name', 'src'],
192
+ 'maction': ['href', 'xref', 'mathcolor', 'mathbackground', 'actiontype', 'selection'],
193
+ 'maligngroup': ['href', 'xref', 'mathcolor', 'mathbackground', 'groupalign'],
194
+ 'malignmark': ['href', 'xref', 'mathcolor', 'mathbackground', 'edge'],
195
+ 'math': ['xmlns', 'href', 'xref', 'display', 'maxwidth', 'overflow', 'altimg', 'altimg-width', 'altimg-height', 'altimg-valign', 'alttext', 'cdgroup', 'mathcolor', 'mathbackground', 'scriptlevel', 'displaystyle', 'scriptsizemultiplier', 'scriptminsize', 'infixlinebreakstyle', 'decimalpoint', 'mathvariant', 'mathsize', 'width', 'height', 'valign', 'form', 'fence', 'separator', 'lspace', 'rspace', 'stretchy', 'symmetric', 'maxsize', 'minsize', 'largeop', 'movablelimits', 'accent', 'linebreak', 'lineleading', 'linebreakstyle', 'linebreakmultchar', 'indentalign', 'indentshift', 'indenttarget', 'indentalignfirst', 'indentshiftfirst', 'indentalignlast', 'indentshiftlast', 'depth', 'lquote', 'rquote', 'linethickness', 'munalign', 'denomalign', 'bevelled', 'voffset', 'open', 'close', 'separators', 'notation', 'subscriptshift', 'superscriptshift', 'accentunder', 'align', 'rowalign', 'columnalign', 'groupalign', 'alignmentscope', 'columnwidth', 'rowspacing', 'columnspacing', 'rowlines', 'columnlines', 'frame', 'framespacing', 'equalrows', 'equalcolumns', 'side', 'minlabelspacing', 'rowspan', 'columnspan', 'edge', 'stackalign', 'charalign', 'charspacing', 'longdivstyle', 'position', 'shift', 'location', 'crossout', 'length', 'leftoverhang', 'rightoverhang', 'mslinethickness', 'selection'],
196
+ 'menclose': ['href', 'xref', 'mathcolor', 'mathbackground', 'notation'],
197
+ 'merror': ['href', 'xref', 'mathcolor', 'mathbackground'],
198
+ 'mfenced': ['href', 'xref', 'mathcolor', 'mathbackground', 'open', 'close', 'separators'],
199
+ 'mfrac': ['href', 'xref', 'mathcolor', 'mathbackground', 'linethickness', 'munalign', 'denomalign', 'bevelled'],
200
+ 'mglyph': ['href', 'xref', 'mathcolor', 'mathbackground', 'src', 'alt', 'width', 'height', 'valign'],
201
+ 'mi': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize'],
202
+ 'mlabeledtr': ['href', 'xref', 'mathcolor', 'mathbackground'],
203
+ 'mlongdiv': ['href', 'xref', 'mathcolor', 'mathbackground', 'longdivstyle', 'align', 'stackalign', 'charalign', 'charspacing'],
204
+ 'mmultiscripts': ['href', 'xref', 'mathcolor', 'mathbackground', 'subscriptshift', 'superscriptshift'],
205
+ 'mn': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize'],
206
+ 'mo': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize', 'form', 'fence', 'separator', 'lspace', 'rspace', 'stretchy', 'symmetric', 'maxsize', 'minsize', 'largeop', 'movablelimits', 'accent', 'linebreak', 'lineleading', 'linebreakstyle', 'linebreakmultchar', 'indentalign', 'indentshift', 'indenttarget', 'indentalignfirst', 'indentshiftfirst', 'indentalignlast', 'indentshiftlast'],
207
+ 'mover': ['href', 'xref', 'mathcolor', 'mathbackground', 'accent', 'align'],
208
+ 'mpadded': ['href', 'xref', 'mathcolor', 'mathbackground', 'height', 'depth', 'width', 'lspace', 'voffset'],
209
+ 'mphantom': ['href', 'xref', 'mathcolor', 'mathbackground'],
210
+ 'mprescripts': ['href', 'xref', 'mathcolor', 'mathbackground'],
211
+ 'mroot': ['href', 'xref', 'mathcolor', 'mathbackground'],
212
+ 'mrow': ['href', 'xref', 'mathcolor', 'mathbackground'],
213
+ 'ms': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize', 'lquote', 'rquote'],
214
+ 'mscarries': ['href', 'xref', 'mathcolor', 'mathbackground', 'position', 'location', 'crossout', 'scriptsizemultiplier'],
215
+ 'mscarry': ['href', 'xref', 'mathcolor', 'mathbackground', 'location', 'crossout'],
216
+ 'msgroup': ['href', 'xref', 'mathcolor', 'mathbackground', 'position', 'shift'],
217
+ 'msline': ['href', 'xref', 'mathcolor', 'mathbackground', 'position', 'length', 'leftoverhang', 'rightoverhang', 'mslinethickness'],
218
+ 'mspace': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize'],
219
+ 'msqrt': ['href', 'xref', 'mathcolor', 'mathbackground'],
220
+ 'msrow': ['href', 'xref', 'mathcolor', 'mathbackground', 'position'],
221
+ 'mstack': ['href', 'xref', 'mathcolor', 'mathbackground', 'align', 'stackalign', 'charalign', 'charspacing'],
222
+ 'mstyle': ['href', 'xref', 'mathcolor', 'mathbackground', 'scriptlevel', 'displaystyle', 'scriptsizemultiplier', 'scriptminsize', 'infixlinebreakstyle', 'decimalpoint', 'mathvariant', 'mathsize', 'width', 'height', 'valign', 'form', 'fence', 'separator', 'lspace', 'rspace', 'stretchy', 'symmetric', 'maxsize', 'minsize', 'largeop', 'movablelimits', 'accent', 'linebreak', 'lineleading', 'linebreakstyle', 'linebreakmultchar', 'indentalign', 'indentshift', 'indenttarget', 'indentalignfirst', 'indentshiftfirst', 'indentalignlast', 'indentshiftlast', 'depth', 'lquote', 'rquote', 'linethickness', 'munalign', 'denomalign', 'bevelled', 'voffset', 'open', 'close', 'separators', 'notation', 'subscriptshift', 'superscriptshift', 'accentunder', 'align', 'rowalign', 'columnalign', 'groupalign', 'alignmentscope', 'columnwidth', 'rowspacing', 'columnspacing', 'rowlines', 'columnlines', 'frame', 'framespacing', 'equalrows', 'equalcolumns', 'side', 'minlabelspacing', 'rowspan', 'columnspan', 'edge', 'stackalign', 'charalign', 'charspacing', 'longdivstyle', 'position', 'shift', 'location', 'crossout', 'length', 'leftoverhang', 'rightoverhang', 'mslinethickness', 'selection'],
223
+ 'msub': ['href', 'xref', 'mathcolor', 'mathbackground', 'subscriptshift'],
224
+ 'msubsup': ['href', 'xref', 'mathcolor', 'mathbackground', 'subscriptshift', 'superscriptshift'],
225
+ 'msup': ['href', 'xref', 'mathcolor', 'mathbackground', 'superscriptshift'],
226
+ 'mtable': ['href', 'xref', 'mathcolor', 'mathbackground', 'align', 'rowalign', 'columnalign', 'groupalign', 'alignmentscope', 'columnwidth', 'width', 'rowspacing', 'columnspacing', 'rowlines', 'columnlines', 'frame', 'framespacing', 'equalrows', 'equalcolumns', 'displaystyle', 'side', 'minlabelspacing'],
227
+ 'mtd': ['href', 'xref', 'mathcolor', 'mathbackground', 'rowspan', 'columnspan', 'rowalign', 'columnalign', 'groupalign'],
228
+ 'mtext': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize', 'width', 'height', 'depth', 'linebreak'],
229
+ 'mtr': ['href', 'xref', 'mathcolor', 'mathbackground', 'rowalign', 'columnalign', 'groupalign'],
230
+ 'munder': ['href', 'xref', 'mathcolor', 'mathbackground', 'accentunder', 'align'],
231
+ 'munderover': ['href', 'xref', 'mathcolor', 'mathbackground', 'accent', 'accentunder', 'align'],
232
+ 'none': ['href', 'xref', 'mathcolor', 'mathbackground'],
233
+ 'semantics': ['href', 'xref', 'definitionURL', 'encoding'],
234
+ 'picture': [],
235
+ 'ruby': [],
236
+ 'rp': [],
237
+ 'rt': [],
238
+ 'g': ['*'],
239
+ 'circle': ['*']
240
+ }),
80
241
  non_empty_elements: 'td th iframe video audio object script a i area base basefont br col frame hr img input isindex link meta param embed source wbr track ruby',
81
242
  // tiny's external link create/edit dialog config
82
243
  target_list: false,
@@ -242,7 +242,7 @@ export function enhanceUserContent(container = document, opts = {}) {
242
242
  iframeElem.setAttribute('src', src.replace('display=in_rce', 'display=borderless'));
243
243
  }
244
244
  });
245
- unenhanced_elem.querySelectorAll('a:not(.not_external, .external)').forEach(childLink => {
245
+ unenhanced_elem.querySelectorAll('a:not(.not_external):not(.external)').forEach(childLink => {
246
246
  if (!isExternalLink(childLink, canvasOrigin)) return;
247
247
  if (childLink.tagName === 'IMG' || childLink.querySelectorAll('img').length > 0) return;
248
248
  childLink.classList.add('external');
@@ -21,6 +21,7 @@ import { showFlashAlert } from '../common/FlashAlert';
21
21
  import { isPreviewable, loadDocPreview, removeLoadingImage, showLoadingImage } from './doc_previews';
22
22
  import { show } from './jqueryish_funcs';
23
23
  import { parseUrlOrNull } from '../util/url-util';
24
+ import psl from 'psl';
24
25
  const youTubeRegEx = /^https?:\/\/(www\.youtube\.com\/watch.*v(=|\/)|youtu\.be\/)([^&#]*)/;
25
26
  export function youTubeID(path) {
26
27
  const match = path.match(youTubeRegEx);
@@ -31,9 +32,12 @@ export function youTubeID(path) {
31
32
  }
32
33
  export function getTld(hostname) {
33
34
  hostname = (hostname || '').split(':')[0];
34
- const parts = hostname.split('.'),
35
- length = parts.length;
36
- return (length > 1 ? [parts[length - 2], parts[length - 1]] : parts).join('.');
35
+ if (hostname.includes('inseng.test')) {
36
+ const parts = hostname.split('.');
37
+ return parts.slice(-3).join('.');
38
+ }
39
+ const parsed = psl.parse(hostname);
40
+ return parsed.domain || hostname;
37
41
  }
38
42
  export function isExternalLink(element, canvasOrigin = window.location.origin) {
39
43
  let canvasHost;
@@ -63,6 +63,7 @@ import buildStyle from './style';
63
63
  import { getMenubarForVariant, getMenuForVariant, getToolbarForVariant, getStatusBarFeaturesForVariant } from './RCEVariants';
64
64
  import { focusFirstMenuButton, focusToolbar, isElementWithinTable, mergeMenu, mergeMenuItems, mergePlugins, mergeToolbar, parsePluginsToExclude, patchAutosavedContent } from './RCEWrapper.utils';
65
65
  import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/util/externalToolsForToolbar';
66
+ import { initScreenreaderOnFormat } from './screenreaderOnFormat';
66
67
  const RestoreAutoSaveModal = /*#__PURE__*/React.lazy(() => import('./RestoreAutoSaveModal'));
67
68
  const RceHtmlEditor = /*#__PURE__*/React.lazy(() => import('./RceHtmlEditor'));
68
69
  const ASYNC_FOCUS_TIMEOUT = 250;
@@ -311,6 +312,7 @@ class RCEWrapper extends React.Component {
311
312
  // @ts-expect-error
312
313
  textarea.value = this.getCode();
313
314
  textarea.style.height = this.state.height;
315
+ textarea.removeAttribute('aria-hidden');
314
316
  if (document.body.classList.contains('Underline-All-Links__enabled')) {
315
317
  if (this.iframe?.contentDocument) {
316
318
  this.iframe.contentDocument.body.classList.add('Underline-All-Links__enabled');
@@ -350,6 +352,7 @@ class RCEWrapper extends React.Component {
350
352
  // focus-trapping components, so they properly ignore trapping focus on click.
351
353
  editor.on('click', () => window.document.body.click(), true);
352
354
  editor.on('Cut Change input Undo Redo', debounce(this.handleInputChange, 1000));
355
+ initScreenreaderOnFormat(editor);
353
356
  this.announceContextToolbars(editor);
354
357
  if (this.isAutoSaving) {
355
358
  this.initAutoSave(editor);
@@ -20,7 +20,7 @@ import React from 'react';
20
20
  import { renderToStaticMarkup } from 'react-dom/server';
21
21
  import { cleanUrl } from './contentInsertionUtils';
22
22
  import formatMessage from '../format-message';
23
- import { VIDEO_SIZE_DEFAULT, AUDIO_PLAYER_SIZE } from './plugins/instructure_record/VideoOptionsTray/TrayController';
23
+ import { videoDefaultSize, AUDIO_PLAYER_SIZE } from './plugins/instructure_record/VideoOptionsTray/TrayController';
24
24
  import { mediaPlayerURLFromFile } from './plugins/shared/fileTypeUtils';
25
25
  import { prepEmbedSrc, prepLinkedSrc, absoluteToRelativeUrl } from '../common/fileUrl';
26
26
  export function renderLink(data, contents, canvasOrigin) {
@@ -97,6 +97,7 @@ export function renderImage(image, canvasOrigin, opts) {
97
97
  }
98
98
  export function renderVideo(video, canvasOrigin) {
99
99
  const src = mediaPlayerURLFromFile(video, canvasOrigin);
100
+ const videoSize = videoDefaultSize();
100
101
  return `
101
102
  <iframe
102
103
  allow="fullscreen"
@@ -105,7 +106,7 @@ export function renderVideo(video, canvasOrigin) {
105
106
  data-media-type="video"
106
107
  loading="lazy"
107
108
  src="${src}"
108
- style="width:${VIDEO_SIZE_DEFAULT.width};height:${VIDEO_SIZE_DEFAULT.height};display:inline-block;"
109
+ style="width:${videoSize.width};height:${videoSize.height};display:inline-block;"
109
110
  title="${formatMessage('Video player for {title}', {
110
111
  title: video.title || video.name || video.text
111
112
  })}"></iframe>
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2019 - present Instructure, Inc.
2
+ * Copyright (C) 2025 - present Instructure, Inc.
3
3
  *
4
4
  * This file is part of Canvas.
5
5
  *
@@ -16,7 +16,7 @@
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- import React, { useState, useEffect } from 'react';
19
+ import React, { useState, useEffect, useRef } from 'react';
20
20
  import { bool, func, number, shape, string } from 'prop-types';
21
21
  import { Button, CloseButton } from '@instructure/ui-buttons';
22
22
  import { Heading } from '@instructure/ui-heading';
@@ -60,6 +60,17 @@ export default function ImageOptionsTray(props) {
60
60
  minWidth: MIN_WIDTH,
61
61
  minPercentage: MIN_PERCENTAGE
62
62
  });
63
+ const [altHasError, setAltHasError] = useState(false);
64
+ const [urlHasError, setUrlHasError] = useState(false);
65
+ const [dimensionsHasError, setDimensionsHasError] = useState(false);
66
+ const urlRef = useRef(null);
67
+ const altRef = useRef(null);
68
+ const dimensionsRef = useRef(null);
69
+ useEffect(() => {
70
+ setUrlHasError(showUrlField && url === '');
71
+ setAltHasError(displayAs === 'embed' && !isDecorativeImage && altText === '');
72
+ setDimensionsHasError(imageSize === CUSTOM && !dimensionsState?.isValid);
73
+ }, [isDecorativeImage, altText, url, displayAs, showUrlField, imageSize, dimensionsState?.isValid]);
63
74
  function handleUrlChange(newUrl) {
64
75
  setUrl(newUrl);
65
76
  }
@@ -72,7 +83,7 @@ export default function ImageOptionsTray(props) {
72
83
  function handleDisplayAsChange(event) {
73
84
  setDisplayAs(event.target.value);
74
85
  }
75
- function handleImageSizeChange(event, selectedOption) {
86
+ function handleImageSizeChange(_e, selectedOption) {
76
87
  setImageSize(selectedOption.value);
77
88
  if (selectedOption.value === CUSTOM) {
78
89
  setImageHeight(currentHeight);
@@ -88,6 +99,18 @@ export default function ImageOptionsTray(props) {
88
99
  }
89
100
  function handleSave(event) {
90
101
  event.preventDefault();
102
+ if (urlHasError || altHasError || dimensionsHasError) {
103
+ if (altHasError) {
104
+ altRef?.current?.focus();
105
+ }
106
+ if (urlHasError) {
107
+ urlRef?.current?.focus();
108
+ }
109
+ if (dimensionsHasError) {
110
+ dimensionsRef?.current?.focus();
111
+ }
112
+ return;
113
+ }
91
114
  const savedAltText = isDecorativeImage ? '' : altText;
92
115
  let appliedHeight = imageHeight;
93
116
  let appliedWidth = imageWidth;
@@ -134,9 +157,6 @@ export default function ImageOptionsTray(props) {
134
157
  type: 'hint'
135
158
  });
136
159
  }
137
- const disableForIcons = isIconMaker && !isDecorativeImage && altText === '';
138
- const disableForImages = url === '' || displayAs === 'embed' && (!isDecorativeImage && altText === '' || imageSize === CUSTOM && !dimensionsState?.isValid);
139
- const saveDisabled = isIconMaker ? disableForIcons : disableForImages;
140
160
  const trayLabel = isIconMaker ? formatMessage('Icon Options Tray') : formatMessage('Image Options Tray');
141
161
  const trayHeading = isIconMaker ? formatMessage('Icon Options') : formatMessage('Image Options');
142
162
  return /*#__PURE__*/React.createElement(Tray, {
@@ -183,7 +203,9 @@ export default function ImageOptionsTray(props) {
183
203
  padding: "small"
184
204
  }, /*#__PURE__*/React.createElement(UrlPanel, {
185
205
  fileUrl: url,
186
- setFileUrl: handleUrlChange
206
+ urlRef: urlRef,
207
+ setFileUrl: handleUrlChange,
208
+ urlHasError: urlHasError
187
209
  })), /*#__PURE__*/React.createElement(ImageOptionsForm, {
188
210
  id: "image-options-form",
189
211
  imageSize: imageSize,
@@ -197,14 +219,16 @@ export default function ImageOptionsTray(props) {
197
219
  handleDisplayAsChange: handleDisplayAsChange,
198
220
  handleImageSizeChange: handleImageSizeChange,
199
221
  messagesForSize: messagesForSize,
200
- isIconMaker: isIconMaker
222
+ isIconMaker: isIconMaker,
223
+ altHasError: altHasError,
224
+ altRef: altRef,
225
+ dimensionsRef: dimensionsRef
201
226
  })), /*#__PURE__*/React.createElement(Flex.Item, {
202
227
  background: "secondary",
203
228
  borderWidth: "small none none none",
204
229
  padding: "small medium",
205
230
  textAlign: "end"
206
231
  }, /*#__PURE__*/React.createElement(Button, {
207
- disabled: saveDisabled,
208
232
  onClick: handleSave,
209
233
  color: "primary"
210
234
  }, formatMessage('Done')))))));
@@ -33,7 +33,7 @@ export declare class RceToolWrapper {
33
33
  width: import("prop-types").Requireable<number>;
34
34
  use_tray: import("prop-types").Requireable<boolean>;
35
35
  canvas_icon_class: import("prop-types").Requireable<any>;
36
- }, "description" | "id" | "name" | "height" | "width" | "favorite" | "on_by_default" | "icon_url" | "use_tray" | "canvas_icon_class">>>)[], mruIds?: string[]): RceToolWrapper[];
36
+ }, "id" | "name" | "width" | "height" | "description" | "favorite" | "on_by_default" | "icon_url" | "use_tray" | "canvas_icon_class">>>)[], mruIds?: string[]): RceToolWrapper[];
37
37
  readonly iconId: string | null | undefined;
38
38
  isMruTool: boolean;
39
39
  get editor(): import("../../types").ExternalToolsEditor | null;
@@ -3,12 +3,22 @@ export namespace VIDEO_SIZE_DEFAULT {
3
3
  let height: string;
4
4
  let width: string;
5
5
  }
6
- export namespace AUDIO_PLAYER_SIZE {
7
- let width_1: string;
8
- export { width_1 as width };
6
+ export namespace STUDIO_PLAYER_VIDEO_SIZE_DEFAULT {
9
7
  let height_1: string;
10
8
  export { height_1 as height };
9
+ let width_1: string;
10
+ export { width_1 as width };
11
+ }
12
+ export namespace AUDIO_PLAYER_SIZE {
13
+ let width_2: string;
14
+ export { width_2 as width };
15
+ let height_2: string;
16
+ export { height_2 as height };
11
17
  }
18
+ export function videoDefaultSize(): {
19
+ height: string;
20
+ width: string;
21
+ };
12
22
  export default class TrayController {
13
23
  _editor: any;
14
24
  _isOpen: boolean;
@@ -28,10 +28,20 @@ export const VIDEO_SIZE_DEFAULT = {
28
28
  height: '225px',
29
29
  width: '400px'
30
30
  }; // AKA "LARGE"
31
+ export const STUDIO_PLAYER_VIDEO_SIZE_DEFAULT = {
32
+ height: '300px',
33
+ width: '480px'
34
+ };
31
35
  export const AUDIO_PLAYER_SIZE = {
32
36
  width: '320px',
33
37
  height: '14.25rem'
34
38
  };
39
+ export const videoDefaultSize = () => {
40
+ if (RCEGlobals.getFeatures().consolidated_media_player) {
41
+ return STUDIO_PLAYER_VIDEO_SIZE_DEFAULT;
42
+ }
43
+ return VIDEO_SIZE_DEFAULT;
44
+ };
35
45
  export default class TrayController {
36
46
  constructor() {
37
47
  this._editor = null;
@@ -25,6 +25,9 @@ declare namespace uploadMediaTranslations {
25
25
  let ADDED_CAPTION: string;
26
26
  let DELETED_CAPTION: string;
27
27
  let PROGRESS_LABEL: string;
28
+ let SELECT_SUPPORTED_FILE_TYPE: string;
29
+ let CHOOSE_FILE_TO_UPLOAD: string;
30
+ let ENTER_FILE_NAME: string;
28
31
  }
29
32
  namespace SelectStrings {
30
33
  let USE_ARROWS: string;
@@ -46,7 +46,10 @@ const uploadMediaTranslations = {
46
46
  ADD_NEW_CAPTION_OR_SUBTITLE: formatMessage('Add another'),
47
47
  ADDED_CAPTION: 'Captions for {lang} added',
48
48
  DELETED_CAPTION: 'Deleted captions for {lang}',
49
- PROGRESS_LABEL: formatMessage('Uploading')
49
+ PROGRESS_LABEL: formatMessage('Uploading'),
50
+ SELECT_SUPPORTED_FILE_TYPE: formatMessage('Please select a file of a supported type'),
51
+ CHOOSE_FILE_TO_UPLOAD: formatMessage('Please choose a file'),
52
+ ENTER_FILE_NAME: formatMessage('Please enter a file name')
50
53
  },
51
54
  SelectStrings: {
52
55
  USE_ARROWS: 'Use arrow keys to navigate options.',
@@ -1,13 +1,15 @@
1
1
  declare function DimensionInput(props: any): React.JSX.Element;
2
2
  declare namespace DimensionInput {
3
3
  namespace propTypes {
4
- let dimensionState: import("prop-types").Requireable<import("prop-types").InferProps<{
4
+ export let dimensionState: import("prop-types").Requireable<import("prop-types").InferProps<{
5
5
  addOffset: import("prop-types").Validator<(...args: any[]) => any>;
6
6
  inputValue: import("prop-types").Validator<string>;
7
7
  setInputValue: import("prop-types").Validator<(...args: any[]) => any>;
8
8
  }>>;
9
- let label: import("prop-types").Validator<string>;
9
+ export { object as dimensionsRef };
10
+ export let label: import("prop-types").Validator<string>;
10
11
  }
11
12
  }
12
13
  export default DimensionInput;
13
14
  import React from 'react';
15
+ import { object } from 'prop-types';
@@ -17,14 +17,15 @@
17
17
  */
18
18
 
19
19
  import React from 'react';
20
- import { func, shape, string } from 'prop-types';
20
+ import { func, shape, string, object } from 'prop-types';
21
21
  import { ScreenReaderContent } from '@instructure/ui-a11y-content';
22
22
  import { NumberInput } from '@instructure/ui-number-input';
23
23
  export default function DimensionInput(props) {
24
24
  const {
25
25
  dimensionState,
26
26
  label,
27
- messages
27
+ messages,
28
+ dimensionsRef
28
29
  } = props;
29
30
  const {
30
31
  addOffset,
@@ -49,7 +50,12 @@ export default function DimensionInput(props) {
49
50
  isRequired: true,
50
51
  showArrows: false,
51
52
  value: inputValue,
52
- messages: messages
53
+ messages: messages,
54
+ inputRef: ref => {
55
+ if (dimensionsRef) {
56
+ dimensionsRef.current = ref;
57
+ }
58
+ }
53
59
  });
54
60
  }
55
61
  DimensionInput.propTypes = {
@@ -58,5 +64,6 @@ DimensionInput.propTypes = {
58
64
  inputValue: string.isRequired,
59
65
  setInputValue: func.isRequired
60
66
  }),
67
+ dimensionsRef: object,
61
68
  label: string.isRequired
62
69
  };
@@ -25,6 +25,7 @@ declare namespace DimensionsInput {
25
25
  export let minWidth: import("prop-types").Validator<number>;
26
26
  export let minPercentage: import("prop-types").Validator<number>;
27
27
  export { bool as hidePercentage };
28
+ export { object as dimensionsRef };
28
29
  }
29
30
  namespace defaultProps {
30
31
  let hidePercentage: boolean;
@@ -34,3 +35,4 @@ export default DimensionsInput;
34
35
  export { default as useDimensionsState } from "./useDimensionsState";
35
36
  import React from 'react';
36
37
  import { bool } from 'prop-types';
38
+ import { object } from 'prop-types';