Give RCEWrapper onFocus/onBlur handlers
closes ADMIN-2742 there are challanges - RCEWrapper keeps track of whether it has focus. This is true if anything w/in its outermost div is the activeElement - since the previously active element blurs before the new element becomes active. we need a timeout to wait and see where focus lands. this is true when: - focus moves to a tinymce popup, like a menu - focus moves to one of RCE's dialogs or trays - the user interacts with content in the CanvasContentTray. This is because a new instance of the CCT is created every time it renders as the user interacts with it. (An artifact of how it's wired into redux). This also addresses a bug where the html-view textarea was the wrong size when flipping between rich text and html views. NOTE: if you close any of the Trays or Modals by typing "esc", it will blur the RCE. This is a known bug that I'm hoping will be fixed via INSTUi-2201. If not, then via another CORE ticket test plan: - not necessary, but if you test in assignments2, you'll know it's working because the RCE will go away if it loses focus, so enable assignments2, create an assignment, then edit the assignment (you can't create an a2 assignment yet) - insert and edit an external link - insert and edit a course image - upload an image - insert and edit a course document - upload a document > in each case, expect focus to return to the RCE, and if applicable, the yellow indicator box is correctly positioned. - in any of the above cases, click on the yellow indicator while it's visible > expect focus to stay w/in the rce resizing: - click the "switch to html view" button > expect the textarea to fill the avaiable space - resize it and click the button to switch back > expect the rce to be the same (or really close) size Change-Id: If85c5644558fbce27530e43bb71c2bdb7e91eb12 Reviewed-on: https://gerrit.instructure.com/199273 Tested-by: Jenkins Reviewed-by: Clay Diffrient <cdiffrient@instructure.com> QA-Review: Jeremy Putnam <jeremyp@instructure.com> Product-Review: Clay Diffrient <cdiffrient@instructure.com>
This commit is contained in:
parent
1b17249ae2
commit
5e5120e566
|
@ -118,7 +118,9 @@ export default class EditableRichText extends React.Component {
|
|||
tinyOptions: {
|
||||
init_instance_callback: this.handleRCEInit,
|
||||
height: 300
|
||||
}
|
||||
},
|
||||
onFocus: this.handleEditorFocus,
|
||||
onBlur: this.handleEditorBlur
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -136,9 +138,6 @@ export default class EditableRichText extends React.Component {
|
|||
handleRCEInit = tinyeditor => {
|
||||
this._tinyeditor = tinyeditor
|
||||
|
||||
this._tinyeditor.on('blur', this.handleEditorBlur)
|
||||
this._tinyeditor.on('focus', this.handleEditorFocus)
|
||||
this._tinyeditor.on('keydown', this.handleKey)
|
||||
document
|
||||
.getElementById('content')
|
||||
.querySelector('[id^="random_editor"]')
|
||||
|
@ -147,18 +146,9 @@ export default class EditableRichText extends React.Component {
|
|||
}
|
||||
|
||||
handleEditorBlur = event => {
|
||||
// Focus isn't managed well in the RCE, so a couple hacks
|
||||
// 1. if the user clicked on a toolbar button that opened a dialog,
|
||||
// the activeElement will be a child of the body, and not the our page
|
||||
// 2. if focus is on the body, then we've left the editor altogether
|
||||
if (
|
||||
document.getElementById('content').contains(document.activeElement) ||
|
||||
document.activeElement === document.body
|
||||
) {
|
||||
if (this._textareaRef) {
|
||||
const txt = RichContentEditor.callOnRCE(this._textareaRef, 'get_code')
|
||||
this.setState({value: txt})
|
||||
}
|
||||
if (this._textareaRef) {
|
||||
const txt = RichContentEditor.callOnRCE(this._textareaRef, 'get_code')
|
||||
this.setState({value: txt})
|
||||
this._onBlurEditor(event)
|
||||
}
|
||||
}
|
||||
|
@ -173,14 +163,6 @@ export default class EditableRichText extends React.Component {
|
|||
this._tinyeditor.selection.collapse(false)
|
||||
}
|
||||
|
||||
handleKey = event => {
|
||||
if (this.props.mode === 'edit' && event.key === 'Escape') {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.handleModeChange('view')
|
||||
}
|
||||
}
|
||||
|
||||
textareaRef = el => {
|
||||
this._textareaRef = el
|
||||
}
|
||||
|
@ -189,9 +171,7 @@ export default class EditableRichText extends React.Component {
|
|||
this._onBlurEditor = onBlur
|
||||
this._editorRef = editorRef
|
||||
editorRef(this)
|
||||
return (
|
||||
<textarea style={{display: 'block'}} defaultValue={this.state.value} ref={this.textareaRef} />
|
||||
)
|
||||
return <textarea defaultValue={this.state.value} ref={this.textareaRef} />
|
||||
}
|
||||
|
||||
// the Editable component thinks I'm the editor
|
||||
|
|
|
@ -63,6 +63,7 @@ export default class KeyboardShortcutModal extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<Modal
|
||||
data-canvas-component
|
||||
open={this.state.isOpen}
|
||||
label={I18n.t('Keyboard Shortcuts')}
|
||||
onDismiss={this.closeModal}
|
||||
|
|
|
@ -40,7 +40,7 @@ function loadServiceRCE(target, tinyMCEInitOptions, callback) {
|
|||
$textarea.data('remoteEditor', remoteEditor)
|
||||
$target.trigger(RCELOADED_EVENT_NAME, remoteEditor)
|
||||
if (callback) {
|
||||
callback()
|
||||
callback(remoteEditor)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -177,13 +177,13 @@ const RichContentEditor = {
|
|||
// avoid modifying the original options object provided
|
||||
tinyMCEInitOptions = $.extend({}, tinyMCEInitOptions)
|
||||
|
||||
const callback = () => {
|
||||
const callback = (rce) => {
|
||||
if (tinyMCEInitOptions.focus) {
|
||||
// call activateRCE once loaded
|
||||
this.activateRCE($target)
|
||||
}
|
||||
if (cb) {
|
||||
cb()
|
||||
cb(rce)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -188,6 +188,7 @@ let loadingPromise
|
|||
language: ENV.LOCALE,
|
||||
mirroredAttrs: this._attrsToMirror(textarea),
|
||||
onFocus: tinyMCEInitOptions.onFocus,
|
||||
onBlur: tinyMCEInitOptions.onBlur,
|
||||
textareaClassName: textarea.className,
|
||||
textareaId: textarea.id,
|
||||
trayProps: getTrayProps()
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* the new rce */
|
||||
.ic-RichContentEditor .rce-wrapper textarea {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.mce-i-a11y:before {
|
||||
content: "\e900";
|
||||
border: 1px solid $ic-font-color-dark;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@instructure/canvas-rce-old": "4.1.4",
|
||||
"@instructure/media-capture": "^5",
|
||||
"@instructure/react-crop": "^5.0.1",
|
||||
"@instructure/ui-alerts": "^5",
|
||||
|
@ -34,8 +35,8 @@
|
|||
"@instructure/ui-forms": "^5",
|
||||
"@instructure/ui-icons": "^5",
|
||||
"@instructure/ui-layout": "^5",
|
||||
"@instructure/ui-menu": "^5",
|
||||
"@instructure/ui-media-player": "^5",
|
||||
"@instructure/ui-menu": "^5",
|
||||
"@instructure/ui-number-input": "^5",
|
||||
"@instructure/ui-overlays": "^5",
|
||||
"@instructure/ui-pagination": "^5",
|
||||
|
@ -57,7 +58,6 @@
|
|||
"big.js": "^5.0.3",
|
||||
"brandable_css": "0.1.0",
|
||||
"canvas-planner": ">=1.0.16",
|
||||
"@instructure/canvas-rce-old": "4.1.4",
|
||||
"canvas_offline_course_viewer": "https://github.com/instructure/canvas_offline_course_viewer.git#1.2.0",
|
||||
"classnames": "^2.2.5",
|
||||
"color-slicer": "0.8.0",
|
||||
|
|
|
@ -48,6 +48,8 @@ function getProps(textareaId, state) {
|
|||
|
||||
textareaClassName: "exampleClassOne",
|
||||
textareaId,
|
||||
onFocus: () => console.log("rce focused"), // eslint-disable-line no-console
|
||||
onBlur: () => console.log("rce blurred"), // eslint-disable-line no-console
|
||||
|
||||
trayProps: {
|
||||
canUploadFiles: true,
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<div class="main">
|
||||
<h2>Editor</h2>
|
||||
<div id="editor1">
|
||||
<textarea id="textarea1"></textarea>
|
||||
<textarea id="textarea1">this is initial text</textarea>
|
||||
</div>
|
||||
<h3>Multiple editors supported with a single sidebar</h3>
|
||||
<div id="editor2">
|
||||
|
|
|
@ -30,7 +30,8 @@ export default function indicate(region, margin = MARGIN) {
|
|||
width: region.width + 2 * margin + "px",
|
||||
height: region.height + 2 * margin + "px",
|
||||
left: region.left - margin + "px",
|
||||
top: region.top - margin + "px"
|
||||
top: region.top - margin + "px",
|
||||
pointerEvents: 'none' // so clicking in the indicator doesn't blur the RCE
|
||||
});
|
||||
|
||||
// start hidden and animate a fade in
|
||||
|
|
|
@ -28,6 +28,7 @@ export default function KeyboardShortcutModal(props) {
|
|||
return (
|
||||
<Modal
|
||||
data-testid="RCE_KeyboardShortcutModal"
|
||||
data-mce-component
|
||||
label={formatMessage('Keyboard Shortcuts')}
|
||||
open={props.open}
|
||||
shouldCloseOnDocumentClick
|
||||
|
|
|
@ -36,6 +36,7 @@ import theme from '../skins/theme'
|
|||
import {isImage} from './plugins/shared/fileTypeUtils'
|
||||
import KeyboardShortcutModal from './KeyboardShortcutModal'
|
||||
|
||||
const ASYNC_FOCUS_TIMEOUT = 250
|
||||
|
||||
// we `require` instead of `import` these 2 css files because the ui-themeable babel require hook only works with `require`
|
||||
const styles = require('../skins/skin-delta.css')
|
||||
|
@ -126,6 +127,7 @@ class RCEWrapper extends React.Component {
|
|||
handleUnmount: PropTypes.func,
|
||||
language: PropTypes.string,
|
||||
onFocus: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
onRemove: PropTypes.func,
|
||||
textareaClassName: PropTypes.string,
|
||||
textareaId: PropTypes.string,
|
||||
|
@ -158,7 +160,8 @@ class RCEWrapper extends React.Component {
|
|||
path: [],
|
||||
wordCount: 0,
|
||||
isHtmlView: false,
|
||||
KBShortcutModalOpen: false
|
||||
KBShortcutModalOpen: false,
|
||||
focused: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,6 +188,14 @@ class RCEWrapper extends React.Component {
|
|||
}
|
||||
|
||||
indicateEditor(element) {
|
||||
if (document.querySelector('[role="dialog"][data-mce-component]')) {
|
||||
// there is a modal open, which zeros out the vertical scroll
|
||||
// so the indicator is in the wrong place. Give it a chance to close
|
||||
window.setTimeout(() => {
|
||||
this.indicateEditor(element)
|
||||
}, 100)
|
||||
return
|
||||
}
|
||||
const editor = this.mceInstance();
|
||||
if (this.indicator) {
|
||||
this.indicator(editor, element);
|
||||
|
@ -350,25 +361,101 @@ class RCEWrapper extends React.Component {
|
|||
return document.getElementById(`${this.props.textareaId}_ifr`)
|
||||
}
|
||||
|
||||
onFocus() {
|
||||
Bridge.focusEditor(this);
|
||||
|
||||
this.props.onFocus && this.props.onFocus(this);
|
||||
// these focus and blur event handlers work together so that RCEWrapper
|
||||
// can report focus and blur events from the RCE at-large
|
||||
get focused() {
|
||||
return this.state.focused
|
||||
}
|
||||
|
||||
reallyOnFocus() {
|
||||
handleFocus() {
|
||||
if (!this.state.focused) {
|
||||
this.setState({focused: true})
|
||||
Bridge.focusEditor(this);
|
||||
this.props.onFocus && this.props.onFocus(this);
|
||||
}
|
||||
}
|
||||
|
||||
contentTrayClosing = false
|
||||
handleContentTrayClosing = isClosing => {
|
||||
this.contentTrayClosing = isClosing
|
||||
}
|
||||
|
||||
blurTimer = 0
|
||||
handleBlur(event) {
|
||||
if (this.blurTimer) return
|
||||
|
||||
if (this.state.focused) {
|
||||
// because the old active element fires blur before the next element gets focus
|
||||
// we often need a moment to see if focus comes back
|
||||
event && event.persist && event.persist()
|
||||
this.blurTimer = window.setTimeout(() => {
|
||||
this.blurTimer = 0
|
||||
if (this.contentTrayClosing) {
|
||||
// the CanvasContentTray is in the process of closing
|
||||
// wait until it finishes
|
||||
return
|
||||
}
|
||||
|
||||
if (this._elementRef && this._elementRef.contains(document.activeElement)) {
|
||||
// focus is still somewhere w/in me
|
||||
return
|
||||
}
|
||||
|
||||
if (document.activeElement.getAttribute('class').includes('tox-')) {
|
||||
// if a toolbar button has focus, then the user clicks on the "more" button
|
||||
// focus jumps to the body, then eventually to the popped pup toolbar. This
|
||||
// catches that case, but could also fail to blur an rce if the user clicked from
|
||||
// one rce on the page to another. I think this is the lesser of the 2 evils
|
||||
return
|
||||
}
|
||||
|
||||
if (event && event.relatedTarget && event.relatedTarget.getAttribute('class').includes('tox-')) {
|
||||
// a tinymce popup has focus
|
||||
return
|
||||
}
|
||||
|
||||
const popup = document.querySelector('[data-mce-component]')
|
||||
if (popup && popup.contains(document.activeElement)) {
|
||||
// one of our popups has focus
|
||||
return
|
||||
}
|
||||
this.setState({focused: false})
|
||||
this.props.onBlur && this.props.onBlur(event)
|
||||
}, ASYNC_FOCUS_TIMEOUT)
|
||||
}
|
||||
}
|
||||
|
||||
handleFocusRCE = event => {
|
||||
if (this._elementRef && !this._elementRef.contains(event.relatedTarget)) {
|
||||
this.handleFocus(event)
|
||||
}
|
||||
}
|
||||
|
||||
handleBlurRCE = event => {
|
||||
if (event.relatedTarget === null) {
|
||||
// focus might be moving to tinymce
|
||||
this.handleBlur(event)
|
||||
}
|
||||
|
||||
if (!this._elementRef.contains(event.relatedTarget)) {
|
||||
this.handleBlur(event)
|
||||
}
|
||||
}
|
||||
|
||||
handleFocusEditor() {
|
||||
// use .active to put a focus ring around the content area
|
||||
// when the editor has focus. This isn't perfect, but it's
|
||||
// what we've got for now.
|
||||
const ifr = this.iframe
|
||||
ifr && ifr.parentElement.classList.add('active')
|
||||
|
||||
this.onFocus()
|
||||
this.handleFocus()
|
||||
}
|
||||
|
||||
onBlur() {
|
||||
handleBlurEditor() {
|
||||
const ifr = this.iframe
|
||||
ifr && ifr.parentElement.classList.remove('active')
|
||||
this.handleBlur(event)
|
||||
}
|
||||
|
||||
call(methodName, ...args) {
|
||||
|
@ -441,7 +528,7 @@ class RCEWrapper extends React.Component {
|
|||
}
|
||||
|
||||
onA11yChecker = () => {
|
||||
this.onTinyMCEInstance('openAccessibilityChecker')
|
||||
this.onTinyMCEInstance('openAccessibilityChecker', {'data-canvas-component': true})
|
||||
}
|
||||
|
||||
handleShortcutKeyShortcut = (event) => {
|
||||
|
@ -462,11 +549,12 @@ class RCEWrapper extends React.Component {
|
|||
|
||||
KBShortcutModalClosed = () => {
|
||||
if(Bridge.activeEditor() === this) {
|
||||
this.onTinyMCEInstance('mceFocus')
|
||||
Bridge.focusActiveEditor(false)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.clearTimeout(this.blurTimer)
|
||||
if (!this._destroyCalled) {
|
||||
this.destroy();
|
||||
}
|
||||
|
@ -556,6 +644,8 @@ class RCEWrapper extends React.Component {
|
|||
componentDidMount() {
|
||||
this.registerTextareaChange();
|
||||
this._elementRef.addEventListener('keyup', this.handleShortcutKeyShortcut, true)
|
||||
// give the textarea its initial size
|
||||
this.onResize(null, {deltaY: 0})
|
||||
}
|
||||
|
||||
componentDidUpdate(_prevProps, prevState) {
|
||||
|
@ -581,7 +671,12 @@ class RCEWrapper extends React.Component {
|
|||
mceProps.editorOptions.statusbar = false
|
||||
|
||||
return (
|
||||
<div ref={el => this._elementRef = el} className={styles.root}>
|
||||
<div
|
||||
className={`${styles.root} rce-wrapper`}
|
||||
ref={el => this._elementRef = el}
|
||||
onFocus={this.handleFocusRCE}
|
||||
onBlur={this.handleBlurRCE}
|
||||
>
|
||||
<ShowOnFocusButton
|
||||
buttonProps={{
|
||||
variant: 'link',
|
||||
|
@ -598,12 +693,12 @@ class RCEWrapper extends React.Component {
|
|||
init={this.wrapOptions(mceProps.editorOptions)}
|
||||
initialValue={mceProps.defaultContent}
|
||||
onInit={this.onInit.bind(this)}
|
||||
onClick={this.onFocus.bind(this)}
|
||||
onKeypress={this.onFocus.bind(this)}
|
||||
onActivate={this.onFocus.bind(this)}
|
||||
onClick={this.handleFocusEditor.bind(this)}
|
||||
onKeypress={this.handleFocusEditor.bind(this)}
|
||||
onActivate={this.handleFocusEditor.bind(this)}
|
||||
onRemove={this.onRemove.bind(this)}
|
||||
onFocus={this.reallyOnFocus.bind(this)}
|
||||
onBlur={this.onBlur.bind(this)}
|
||||
onFocus={this.handleFocusEditor.bind(this)}
|
||||
onBlur={this.handleBlurEditor.bind(this)}
|
||||
onNodeChange={this.onNodeChange}
|
||||
/>
|
||||
<StatusBar
|
||||
|
@ -615,7 +710,7 @@ class RCEWrapper extends React.Component {
|
|||
onKBShortcutModalOpen={this.openKBShortcutModal}
|
||||
onA11yChecker={this.onA11yChecker}
|
||||
/>
|
||||
<CanvasContentTray bridge={Bridge} {...trayProps} />
|
||||
<CanvasContentTray bridge={Bridge} onTrayClosing={this.handleContentTrayClosing} {...trayProps} />
|
||||
<KeyboardShortcutModal
|
||||
onClose={this.KBShortcutModalClosed}
|
||||
onDismiss={this.closeKBShortcutModal}
|
||||
|
|
|
@ -29,7 +29,10 @@ export default function(ed, document) {
|
|||
document.body.appendChild(container)
|
||||
}
|
||||
|
||||
const handleDismiss = () => ReactDOM.unmountComponentAtNode(container)
|
||||
const handleDismiss = () => {
|
||||
ReactDOM.unmountComponentAtNode(container)
|
||||
ed.focus()
|
||||
}
|
||||
|
||||
// acccept=undefined -> can upload anything
|
||||
ReactDOM.render(
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
import bridge from '../../../../bridge'
|
||||
import ImageOptionsTray from '.'
|
||||
|
||||
export const CONTAINER_ID = 'instructure-image-options-tray-container'
|
||||
|
@ -106,6 +107,7 @@ export default class TrayController {
|
|||
this._isOpen = true
|
||||
}}
|
||||
onExited={() => {
|
||||
bridge.focusActiveEditor(false)
|
||||
this._isOpen = false
|
||||
}}
|
||||
onSave={imageOptions => {
|
||||
|
|
|
@ -105,6 +105,7 @@ export default function ImageOptionsTray(props) {
|
|||
|
||||
return (
|
||||
<Tray
|
||||
data-mce-component
|
||||
label={formatMessage('Image Options Tray')}
|
||||
onDismiss={onRequestClose}
|
||||
onEntered={props.onEntered}
|
||||
|
|
|
@ -59,6 +59,7 @@ export default class LinkOptionsDialogController {
|
|||
this._renderDialog()
|
||||
}
|
||||
_hasClosed = () => {
|
||||
bridge.focusActiveEditor(false)
|
||||
this._isOpen = false
|
||||
this._editor.focus(false)
|
||||
this._editor = null
|
||||
|
|
|
@ -60,6 +60,7 @@ export default function LinkOptionsDialog(props) {
|
|||
return (
|
||||
<Modal
|
||||
data-testid="RCELinkOptionsDialog"
|
||||
data-mce-component
|
||||
as="form"
|
||||
label={label}
|
||||
onDismiss={props.onRequestClose}
|
||||
|
|
|
@ -78,6 +78,7 @@ export default class LinkOptionsTrayController {
|
|||
this._isOpen = true
|
||||
}}
|
||||
onExited={() => {
|
||||
bridge.focusActiveEditor(false)
|
||||
this._isOpen = false
|
||||
}}
|
||||
onSave={linkOptions => {
|
||||
|
|
|
@ -72,6 +72,7 @@ export default function LinkOptionsTray(props) {
|
|||
return (
|
||||
<Tray
|
||||
data-testid="RCELinkOptionsTray"
|
||||
data-mce-component
|
||||
label={formatMessage('Link Options')}
|
||||
onDismiss={props.onRequestClose}
|
||||
onEntered={props.onEntered}
|
||||
|
|
|
@ -111,12 +111,21 @@ export default function CanvasContentTray(props) {
|
|||
}, [props.bridge])
|
||||
|
||||
function handleDismissTray() {
|
||||
props.onTrayClosing && props.onTrayClosing(true) // tell RCEWrapper we're closing
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
function handleExitTray() {
|
||||
props.onTrayClosing && props.onTrayClosing(true) // tell RCEWrapper we're closing
|
||||
}
|
||||
|
||||
function handleCloseTray() {
|
||||
props.bridge.focusActiveEditor(false)
|
||||
// increment a counter that's used a the key when rendering
|
||||
// this gets us a new instance everytime, which is necessary
|
||||
// to get the queries run so we have up to date data.
|
||||
setOpenCount(openCount + 1)
|
||||
props.onTrayClosing && props.onTrayClosing(false) // tell RCEWrapper we're closed
|
||||
}
|
||||
|
||||
function renderLoading() {
|
||||
|
@ -127,6 +136,7 @@ export default function CanvasContentTray(props) {
|
|||
<StoreProvider {...props} key={openCount}>
|
||||
{contentProps => (
|
||||
<Tray
|
||||
data-mce-component
|
||||
data-testid="CanvasContentTray"
|
||||
label={getTrayLabel(filterSettings)}
|
||||
open={isOpen}
|
||||
|
@ -137,6 +147,7 @@ export default function CanvasContentTray(props) {
|
|||
shouldCloseOnDocumentClick
|
||||
onDismiss={handleDismissTray}
|
||||
onClose={handleCloseTray}
|
||||
onExit={handleExitTray}
|
||||
>
|
||||
<Flex direction="column" display="block" height="100vh" overflowY="hidden">
|
||||
<Flex.Item padding="medium" shadow="above">
|
||||
|
@ -193,6 +204,7 @@ export const trayProps = shape(trayPropsMap)
|
|||
|
||||
CanvasContentTray.propTypes = {
|
||||
bridge: instanceOf(Bridge).isRequired,
|
||||
onTrayClosing: func, // called with true when the tray starts closing, false once closed
|
||||
...trayPropsMap
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ export function UploadFile({accept, editor, label, panels, onDismiss, trayProps,
|
|||
<StoreProvider {...trayProps}>
|
||||
{contentProps => (
|
||||
<Modal
|
||||
data-mce-component
|
||||
as="form"
|
||||
label={label}
|
||||
size="large"
|
||||
|
|
|
@ -31,7 +31,15 @@
|
|||
.root {
|
||||
background-color: var(--canvasBackgroundColor)
|
||||
}
|
||||
|
||||
:global {
|
||||
/* not part of the skin, but necessary to properly size the RCE's textarea */
|
||||
.rce-wrapper textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.tox,
|
||||
.tox *:not(svg) {
|
||||
color: inherit;
|
||||
|
|
|
@ -20,7 +20,6 @@ import assert from "assert";
|
|||
import jsdomify from "jsdomify";
|
||||
import sinon from "sinon";
|
||||
import Bridge from "../../src/bridge";
|
||||
import ReactDOM from "react-dom";
|
||||
import * as indicateModule from "../../src/common/indicate";
|
||||
import * as contentInsertion from "../../src/rce/contentInsertion";
|
||||
import RCEWrapper from "../../src/rce/RCEWrapper";
|
||||
|
@ -551,22 +550,19 @@ describe("RCEWrapper", () => {
|
|||
|
||||
it("calls Bridge.focusEditor with editor", () => {
|
||||
const editor = createBasicElement();
|
||||
editor.onFocus();
|
||||
editor.handleFocus();
|
||||
sinon.assert.calledWith(Bridge.focusEditor, editor);
|
||||
});
|
||||
|
||||
it("calls props.onFocus with editor if exists", () => {
|
||||
const editor = createBasicElement({ onFocus: sinon.spy() });
|
||||
editor.onFocus();
|
||||
editor.handleFocus();
|
||||
sinon.assert.calledWith(editor.props.onFocus, editor);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onRemove", () => {
|
||||
let domNode;
|
||||
|
||||
beforeEach(() => {
|
||||
domNode = {};
|
||||
sinon.stub(Bridge, "detachEditor");
|
||||
});
|
||||
|
||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -16921,9 +16921,9 @@ resolve@1.1.7, resolve@~1.1.0:
|
|||
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
|
||||
|
||||
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232"
|
||||
integrity sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e"
|
||||
integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==
|
||||
dependencies:
|
||||
path-parse "^1.0.6"
|
||||
|
||||
|
@ -18756,9 +18756,9 @@ tinycolor2@1.4.1, tinycolor2@^1.4.1:
|
|||
integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=
|
||||
|
||||
tinymce-a11y-checker@^2:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tinymce-a11y-checker/-/tinymce-a11y-checker-2.1.1.tgz#9cc43e87943e83318324ce08af30463a610cc0fc"
|
||||
integrity sha512-CzhgSYvGq5W75drXJ57eKg1o2IP0HGN1BgiU8Z5etdvzVnPx7myI1aT/lPehNPVmDZZmPPtDNcSphiG21Va4yQ==
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tinymce-a11y-checker/-/tinymce-a11y-checker-2.2.0.tgz#8ac8ad0fa298b2c24ad025ca3345199ce18486f3"
|
||||
integrity sha512-0GSXRNZa8zhj/f1S0DGRNRPnhJ7SDg5rMRqTlSrKOb9El6eR8JxokE/jc2OPLtlCioJMhre6KUUiV8G0tlWAAQ==
|
||||
dependencies:
|
||||
"@instructure/ui-buttons" "^5"
|
||||
"@instructure/ui-core" "^5"
|
||||
|
@ -19101,9 +19101,9 @@ typescript@^3.3.3:
|
|||
integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
|
||||
|
||||
ua-parser-js@^0.7.18, ua-parser-js@^0.7.9:
|
||||
version "0.7.19"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
|
||||
integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
|
||||
version "0.7.20"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098"
|
||||
integrity sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==
|
||||
|
||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||
version "1.0.6"
|
||||
|
|
Loading…
Reference in New Issue