From 1bc3f6c94a32e8b2683e3c43a5c0d7bf7628d345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ian=20Ramirez-Espa=C3=B1a?= Date: Sat, 10 Jul 2021 14:05:35 -0500 Subject: [PATCH] Revert "Merge branch 'instructure:master' into master" This reverts commit d7ff21366c6ee16b080698703d429f5e6d4c712f, reversing changes made to 78be0ec068869f658246063b2b07b3cc866611b1. --- jest.config.js | 5 +- jest/jest-setup.js | 8 +- packages/canvas-rce/demo/app.js | 14 +- .../src/rce/{RCE.js => CanvasRce.js} | 38 +++- packages/canvas-rce/src/rce/RCEWrapper.js | 54 ++--- packages/canvas-rce/src/rce/StatusBar.js | 7 +- .../src/rce/__mocks__/tinymceReact.js | 7 +- .../{RCE.test.js => CanvasRce.test.js} | 10 +- .../canvas-rce/test/module/RCEWrapper.test.js | 3 +- ui/shared/rce/react/CanvasRce.js | 16 +- .../rce/react/__tests__/CanvasRce.test.js | 201 ------------------ ui/shared/rce/tinymce.config.js | 26 +-- 12 files changed, 83 insertions(+), 306 deletions(-) rename packages/canvas-rce/src/rce/{RCE.js => CanvasRce.js} (85%) rename packages/canvas-rce/src/rce/__tests__/{RCE.test.js => CanvasRce.test.js} (86%) delete mode 100644 ui/shared/rce/react/__tests__/CanvasRce.test.js diff --git a/jest.config.js b/jest.config.js index 6c1fde3de2bd..61105a656b47 100644 --- a/jest.config.js +++ b/jest.config.js @@ -27,9 +27,10 @@ module.exports = { '^Backbone$': '/public/javascripts/Backbone.js', // jest can't import the icons '@instructure/ui-icons/es/svg': '/packages/canvas-rce/src/rce/__tests__/_mockIcons.js', - // redirect imports from es/rce to lib + // redirect import from es/rce/CanvasRce to lib + '@instructure/canvas-rce/es/rce/CanvasRce': + '/packages/canvas-rce/lib/rce/CanvasRce.js', '@instructure/canvas-rce/es/rce/tinyRCE': '/packages/canvas-rce/lib/rce/tinyRCE.js', - '@instructure/canvas-rce/es/rce/RCE': '/packages/canvas-rce/lib/rce/RCE.js', // mock the tinymce-react Editor react component '@tinymce/tinymce-react': '/packages/canvas-rce/src/rce/__mocks__/tinymceReact.js' }, diff --git a/jest/jest-setup.js b/jest/jest-setup.js index 9ff6380412bf..6545324c49be 100644 --- a/jest/jest-setup.js +++ b/jest/jest-setup.js @@ -25,9 +25,7 @@ filterUselessConsoleMessages(console) require('jest-fetch-mock').enableFetchMocks() window.scroll = () => {} -window.ENV = { - use_rce_enhancements: true -} +window.ENV = {} Enzyme.configure({adapter: new Adapter()}) @@ -77,8 +75,8 @@ if (process.env.DEPRECATION_SENTRY_DSN) { } }).install() - const setupRavenConsoleLoggingPlugin = - require('../ui/boot/initializers/setupRavenConsoleLoggingPlugin').default + const setupRavenConsoleLoggingPlugin = require('../app/jsx/shared/helpers/setupRavenConsoleLoggingPlugin') + .default setupRavenConsoleLoggingPlugin(Raven, {loggerName: 'console-jest'}) } diff --git a/packages/canvas-rce/demo/app.js b/packages/canvas-rce/demo/app.js index 4df0e2e75d73..a827c4ded100 100644 --- a/packages/canvas-rce/demo/app.js +++ b/packages/canvas-rce/demo/app.js @@ -18,7 +18,7 @@ import React, {useEffect, useRef, useState} from 'react' import ReactDOM from 'react-dom' -import RCE from '../src/rce/RCE' +import CanvasRce from '../src/rce/CanvasRce' import DemoOptions from './DemoOptions' import {Button} from '@instructure/ui-buttons' import {View} from '@instructure/ui-view' @@ -183,20 +183,18 @@ function Demo() { return ( <>
- { setCurrentContent(editor.getContent()) }} diff --git a/packages/canvas-rce/src/rce/RCE.js b/packages/canvas-rce/src/rce/CanvasRce.js similarity index 85% rename from packages/canvas-rce/src/rce/RCE.js rename to packages/canvas-rce/src/rce/CanvasRce.js index f6216590752d..9d3b1fd8ec5b 100644 --- a/packages/canvas-rce/src/rce/RCE.js +++ b/packages/canvas-rce/src/rce/CanvasRce.js @@ -17,9 +17,9 @@ */ import React, {forwardRef, useState} from 'react' -import {arrayOf, bool, func, number, objectOf, oneOfType, shape, string} from 'prop-types' +import {arrayOf, bool, func, number, object, objectOf, oneOfType, shape, string} from 'prop-types' import formatMessage from '../format-message' -import RCEWrapper, {editorOptionsPropType, ltiToolsPropType} from './RCEWrapper' +import RCEWrapper, {toolbarPropType, menuPropType, ltiToolsPropType} from './RCEWrapper' import {trayPropTypes} from './plugins/shared/CanvasContentTray' import editorLanguage from './editorLanguage' import normalizeLocale from './normalizeLocale' @@ -40,7 +40,7 @@ if (!process?.env?.BUILD_LOCALE) { // forward rceRef to it refs the RCEWrapper where clients can call getCode etc. on it. // You probably shouldn't use it until onInit has been called. Until then tinymce // is not initialized. -const RCE = forwardRef(function RCE(props, rceRef) { +const CanvasRce = forwardRef(function CanvasRce(props, rceRef) { const { autosave, defaultContent, @@ -51,10 +51,13 @@ const RCE = forwardRef(function RCE(props, rceRef) { language, liveRegion, mirroredAttrs, // attributes to transfer from the original textarea to the one created by tinymce + menu, + plugins, readOnly, textareaId, textareaClassName, rcsProps, + toolbar, use_rce_pretty_html_editor, use_rce_buttons_and_icons, onFocus, @@ -93,15 +96,23 @@ const RCE = forwardRef(function RCE(props, rceRef) { instRecordDisabled, language: normalizeLocale(language), liveRegion, + menu, + plugins, textareaId, textareaClassName, trayProps: rcsProps, + toolbar, use_rce_pretty_html_editor, use_rce_buttons_and_icons, editorOptions: Object.assign(editorOptions, editorOptions, { selector: `#${textareaId}`, height, - language: editorLanguage(props.language) + language: editorLanguage(props.language), + toolbar: props.toolbar, + menu: props.menu, + menubar: props.menu ? Object.keys(props.menu).join(' ') : undefined, + plugins: props.plugins, + readonly: readOnly }) } wrapInitCb(mirroredAttrs, iProps.editorOptions) @@ -128,9 +139,9 @@ const RCE = forwardRef(function RCE(props, rceRef) { } }) -export default RCE +export default CanvasRce -RCE.propTypes = { +CanvasRce.propTypes = { // do you want the rce to autosave content to localStorage, and // how long should it be until it's deleted. // If autosave is enabled, call yourRef.RCEClosed() if the user @@ -138,9 +149,8 @@ RCE.propTypes = { autosave: shape({enabled: bool, maxAge: number}), // the initial content defaultContent: string, - // tinymce configuration. See defaultTinymceConfig for all the defaults - // and RCEWrapper.editorOptionsPropType for stuff you may want to include - editorOptions: editorOptionsPropType, + // tinymce configuration. See defaultTinymceConfig for the basics + editorOptions: object, // height of the RCE. if a number, in px height: oneOfType([number, string]), // array of URLs to high-contrast css @@ -169,6 +179,10 @@ RCE.propTypes = { // name:value pairs of attributes to add to the textarea // tinymce creates as the backing store of the RCE mirroredAttrs: objectOf(string), + // additional menu items that get merged into the default menubar + menu: menuPropType, + // additional plugins that get merged into the default list of plugins + plugins: arrayOf(string), // is this RCE readonly? readOnly: bool, // id put on the generated textarea @@ -178,6 +192,8 @@ RCE.propTypes = { // properties necessary for the RCE to us the RCS // if missing, RCE features that require the RCS are omitted rcsProps: trayPropTypes, + // additional toolbar items that get merged into the default toolbars + toolbar: toolbarPropType, // enable the pretty html editor (temporary until the feature is forced on) use_rce_pretty_html_editor: bool, // enable the custom buttons feature (temporary until the feature is forced on) @@ -186,10 +202,10 @@ RCE.propTypes = { onFocus: func, // f(RCEWrapper component) onBlur: func, // f(event) onInit: func, // f(tinymce_editor) - onContentChange: func // f(content), don't mistake this as an indication RCE is a controlled component + onContentChange: func // f(content), don't mistake this as an indication CanvasRce is a controlled component } -RCE.defaultProps = { +CanvasRce.defaultProps = { autosave: {enabled: false, maxAge: 3600000}, defaultContent: '', editorOptions: {...defaultTinymceConfig}, diff --git a/packages/canvas-rce/src/rce/RCEWrapper.js b/packages/canvas-rce/src/rce/RCEWrapper.js index 97b80d33ec67..4c2bbb0c592b 100644 --- a/packages/canvas-rce/src/rce/RCEWrapper.js +++ b/packages/canvas-rce/src/rce/RCEWrapper.js @@ -61,10 +61,6 @@ const toolbarPropType = PropTypes.arrayOf( PropTypes.shape({ // name of the toolbar the items are added to // if this toolbar doesn't exist, it is created - // tinymce toolbar config does not - // include a key to identify the individual toolbars, just a name - // which is translated. This toolbar's name must be translated - // in order to be merged correctly. name: PropTypes.string.isRequired, // items added to the toolbar // each is the name of the button some plugin has @@ -74,11 +70,10 @@ const toolbarPropType = PropTypes.arrayOf( ) const menuPropType = PropTypes.objectOf( - // the key is the name of the menu item a plugin has - // registered with tinymce. If it does not exist in the - // default menubar, it will be added. + // the key is the name of the menu item some plugin has + // registered with tinymce PropTypes.shape({ - // if this is a new menu in the menubar, title is it's label. + // if this is a new menu in the menubar,title it's label. // if these are items being merged into an existing menu, title is ignored title: PropTypes.string, // items is a space separated list it menu_items @@ -95,26 +90,6 @@ const ltiToolsPropType = PropTypes.arrayOf( }) ) -export const editorOptionsPropType = PropTypes.shape({ - // height of the RCE. - // if a number interpreted as pixels. - // if a string as a CSS value. - height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - // entries you want merged into the toolbar. See toolBarPropType above. - toolbar: toolbarPropType, - // entries you want merged into to the menus. See menuPropType above. - // If an entry defines a new menu, tinymce's menubar config option will - // be updated for you. In fact, if you provide an editorOptions.menubar value - // it will be overwritten. - menu: menuPropType, - // additional plugins that get merged into the default list of plugins - // it is up to you to import the plugin's definition which will - // register it and any related toolbar or menu entries with tinymce. - plugins: PropTypes.arrayOf(PropTypes.string), - // is this RCE readonly? - readonly: PropTypes.bool -}) - // we `require` instead of `import` because the ui-themeable babel require hook only works with `require` // 2021-04-21: This is no longer true, but I didn't want to make a gratutious change when I found this out. // see https://gerrit.instructure.com/c/canvas-lms/+/263299/2/packages/canvas-rce/src/rce/RCEWrapper.js#50 @@ -241,7 +216,7 @@ class RCEWrapper extends React.Component { maxAge: PropTypes.number }), defaultContent: PropTypes.string, - editorOptions: editorOptionsPropType, + editorOptions: PropTypes.object, handleUnmount: PropTypes.func, editorView: PropTypes.oneOf([WYSIWYG_VIEW, PRETTY_HTML_EDITOR_VIEW, RAW_HTML_EDITOR_VIEW]), id: PropTypes.string, @@ -966,10 +941,6 @@ class RCEWrapper extends React.Component { // first view this.setEditorView(this.state.editorView) - // readonly should have been handled via the init property passed - // to , but it's not. - editor.mode.set(this.props.readOnly ? 'readonly' : 'design') - this.props.onInitted?.(editor) } @@ -1332,15 +1303,9 @@ class RCEWrapper extends React.Component { canvasPlugins.push('instructure_buttons') } - const possibleNewMenubarItems = this.props.editorOptions.menu - ? Object.keys(this.props.editorOptions.menu).join(' ') - : undefined - const wrappedOpts = { ...options, - readonly: this.props.readOnly, - theme: 'silver', // some older code specified 'modern', which doesn't exist any more height: options.height || DEFAULT_RCE_HEIGHT, @@ -1378,7 +1343,7 @@ class RCEWrapper extends React.Component { content_css: options.content_css || [], content_style: contentCSS, - menubar: mergeMenuItems('edit view insert format tools table', possibleNewMenubarItems), + menubar: mergeMenuItems('edit view insert format tools table', options.menubar), // default menu options listed at https://www.tiny.cloud/docs/configure/editor-appearance/#menu // tinymce's default edit and table menus are fine // we include all the canvas specific items in the menu and toolbar @@ -1468,13 +1433,14 @@ class RCEWrapper extends React.Component { 'instructure_media_embed', 'instructure_external_tools', 'a11y_checker', - 'wordcount', - ...canvasPlugins + 'wordcount' ], sanitizePlugins(options.plugins) ) } + wrappedOpts.plugins.splice(wrappedOpts.plugins.length, 0, ...canvasPlugins) + if (this.props.trayProps) { wrappedOpts.canvas_rce_user_context = { type: this.props.trayProps.contextType, @@ -1774,6 +1740,10 @@ function mergeMenu(standard, custom) { // returns: the merged result by mutating the incoming standard arg. // It will add commands to existing toolbars, or add a new toolbar // if the custom one does not exist +// This is a little awkward in that tinymce toolbar config does not +// include a key to identify the individual toolbars, just a name +// which is translated. The custom toolbar's name must be translated +// in order to be merged correctly. function mergeToolbar(standard, custom) { if (!custom) return standard // merge given toolbar data into the default toolbar diff --git a/packages/canvas-rce/src/rce/StatusBar.js b/packages/canvas-rce/src/rce/StatusBar.js index 730c0cb1dad3..20e1b7a0a2bc 100644 --- a/packages/canvas-rce/src/rce/StatusBar.js +++ b/packages/canvas-rce/src/rce/StatusBar.js @@ -21,9 +21,10 @@ import ReactDOM from 'react-dom' import {arrayOf, bool, func, number, oneOf, string} from 'prop-types' import {StyleSheet, css} from 'aphrodite' import keycode from 'keycode' -import {CondensedButton, IconButton} from '@instructure/ui-buttons' +import {Button, CondensedButton, IconButton} from '@instructure/ui-buttons' import {Flex} from '@instructure/ui-flex' import {View} from '@instructure/ui-view' +import {ScreenReaderContent} from '@instructure/ui-a11y-content' import {Text} from '@instructure/ui-text' import {SVGIcon} from '@instructure/ui-svg-images' @@ -111,11 +112,9 @@ export default function StatusBar(props) { } // adding a delay before including the HTML Editor description to wait the focus moves to the RCE // and prevent JAWS from reading the aria-describedby element when switching back to RCE view - const timerid = setTimeout(() => { + setTimeout(() => { setIncludeEdtrDesc(props.use_rce_pretty_html_editor && !isHtmlView()) }, 100) - - return () => clearTimeout(timerid) }, [props.editorView]) // eslint-disable-line react-hooks/exhaustive-deps function preferredHtmlEditor() { diff --git a/packages/canvas-rce/src/rce/__mocks__/tinymceReact.js b/packages/canvas-rce/src/rce/__mocks__/tinymceReact.js index 720c3960325b..8bd4cc612636 100644 --- a/packages/canvas-rce/src/rce/__mocks__/tinymceReact.js +++ b/packages/canvas-rce/src/rce/__mocks__/tinymceReact.js @@ -28,10 +28,9 @@ import React, {useEffect, useRef} from 'react' class FakeEditor { - constructor(props) { - this.props = props + constructor(textareaId) { this.hidden = true - this._textareaId = props.id + this._textareaId = textareaId this.readonly = undefined this._eventHandlers = {} } @@ -95,7 +94,7 @@ class FakeEditor { export function Editor(props) { const editorRef = useRef(null) const textareaRef = useRef(null) - const tinymceEditor = useRef(new FakeEditor(props)) + const tinymceEditor = useRef(new FakeEditor(props.id)) useEffect(() => { window.tinymce.editors[0] = tinymceEditor.current diff --git a/packages/canvas-rce/src/rce/__tests__/RCE.test.js b/packages/canvas-rce/src/rce/__tests__/CanvasRce.test.js similarity index 86% rename from packages/canvas-rce/src/rce/__tests__/RCE.test.js rename to packages/canvas-rce/src/rce/__tests__/CanvasRce.test.js index 491cf5c47b2d..c49b874f5783 100644 --- a/packages/canvas-rce/src/rce/__tests__/RCE.test.js +++ b/packages/canvas-rce/src/rce/__tests__/CanvasRce.test.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 - present Instructure, Inc. + * Copyright (C) 2018 - present Instructure, Inc. * * This file is part of Canvas. * @@ -18,10 +18,10 @@ import React, {createRef} from 'react' import {render, waitFor} from '@testing-library/react' -import RCE from '../RCE' +import CanvasRce from '../CanvasRce' import bridge from '../../bridge' -describe('RCE', () => { +describe('CanvasRce', () => { let target beforeEach(() => { @@ -38,13 +38,13 @@ describe('RCE', () => { }) it('bridges newly rendered editors', async () => { - render(, target) + render(, target) await waitFor(() => expect(bridge.activeEditor().constructor.displayName).toEqual('RCEWrapper')) }) it('supports getCode() and setCode() on its ref', async () => { const rceRef = createRef(null) - render(, target) + render(, target) await waitFor(() => expect(rceRef.current).not.toBeNull()) diff --git a/packages/canvas-rce/test/module/RCEWrapper.test.js b/packages/canvas-rce/test/module/RCEWrapper.test.js index c65363fb1f72..0388b9eef59c 100644 --- a/packages/canvas-rce/test/module/RCEWrapper.test.js +++ b/packages/canvas-rce/test/module/RCEWrapper.test.js @@ -91,8 +91,7 @@ function defaultProps() { highContrastCSS: [], languages: [{id: 'en', label: 'English'}], autosave: {enabled: false}, - ltiTools: [], - editorOptions: {} + ltiTools: [] } } diff --git a/ui/shared/rce/react/CanvasRce.js b/ui/shared/rce/react/CanvasRce.js index 673a8d5fed96..e046378b85b2 100644 --- a/ui/shared/rce/react/CanvasRce.js +++ b/ui/shared/rce/react/CanvasRce.js @@ -19,7 +19,7 @@ import React, {forwardRef, useEffect, useState} from 'react' import {bool, func, number, object, objectOf, oneOfType, string} from 'prop-types' import {createChainedFunction} from '@instructure/ui-utils' -import RCE from '@instructure/canvas-rce/es/rce/RCE' +import TheRealRce from '@instructure/canvas-rce/es/rce/CanvasRce' import getRCSProps from '../getRCSProps' import closedCaptionLanguages from '@canvas/util/closedCaptionLanguages' import EditorConfig from '../tinymce.config' @@ -67,12 +67,10 @@ const CanvasRce = forwardRef(function CanvasRce(props, rceRef) { // tinymce is a global by now via import of CanvasRce importing tinyRCE const editorConfig = new EditorConfig(tinymce, window.INST, textareaId) const config = {...editorConfig.defaultConfig(), ...editorOptions} - if (editorOptions.init_instance_callback) { - config.init_instance_callback = createChainedFunction( - config.init_instance_callback, - editorOptions.init_instance_callback - ) - } + config.init_instance_callback = createChainedFunction( + config.init_instance_callback, + editorOptions.init_instance_callback + ) return config }) const [autosave_] = useState({ @@ -88,7 +86,7 @@ const CanvasRce = forwardRef(function CanvasRce(props, rceRef) { }, [rceRef]) return ( - . - */ - -import React, {createRef} from 'react' -import {render, waitFor} from '@testing-library/react' -import CanvasRce from '../CanvasRce' - -describe('CanvasRce', () => { - let target - - beforeEach(() => { - const div = document.createElement('div') - div.id = 'fixture' - div.innerHTML = '