diff --git a/app/jsx/editor/ExternalToolDialog.js b/app/jsx/editor/ExternalToolDialog.js
index f812b60dd8e..77adcd65a9e 100644
--- a/app/jsx/editor/ExternalToolDialog.js
+++ b/app/jsx/editor/ExternalToolDialog.js
@@ -54,7 +54,8 @@ export default class ExternalToolDialog extends React.Component {
selection: PropTypes.shape({
getContent: PropTypes.func.isRequired
}),
- getContent: PropTypes.func.isRequired
+ getContent: PropTypes.func.isRequired,
+ focus: PropTypes.func.isRequired
}).isRequired,
contextAssetString: PropTypes.string.isRequired,
iframeAllowances: PropTypes.string.isRequired,
@@ -140,6 +141,7 @@ export default class ExternalToolDialog extends React.Component {
handleRemove = () => {
this.setState({button: EMPTY_BUTTON})
+ this.props.editor.focus()
}
handleInfoAlertFocus = ev => this.setState({infoAlert: ev.target})
diff --git a/app/jsx/editor/__tests__/ExternalToolDialog.test.js b/app/jsx/editor/__tests__/ExternalToolDialog.test.js
index 3eaf38185ef..9f09758926b 100644
--- a/app/jsx/editor/__tests__/ExternalToolDialog.test.js
+++ b/app/jsx/editor/__tests__/ExternalToolDialog.test.js
@@ -68,7 +68,8 @@ function fakeEditor() {
selection: {
getContent: jest.fn()
},
- getContent: jest.fn()
+ getContent: jest.fn(),
+ focus: () => {}
}
}
diff --git a/app/models/context_external_tool.rb b/app/models/context_external_tool.rb
index 2dbc77ff99a..ed755bfb9cf 100644
--- a/app/models/context_external_tool.rb
+++ b/app/models/context_external_tool.rb
@@ -873,6 +873,7 @@ end
def self.editor_button_json(tools, context, user, session=nil)
tools.select! {|tool| visible?(tool.editor_button['visibility'], user, context, session)}
+ markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new({link_attributes: {target: '_blank'}}))
tools.map do |tool|
{
:name => tool.label_for(:editor_button, I18n.locale),
@@ -882,7 +883,12 @@ end
:canvas_icon_class => tool.editor_button(:canvas_icon_class),
:width => tool.editor_button(:selection_width),
:height => tool.editor_button(:selection_height),
- :use_tray => tool.editor_button(:use_tray) == "true"
+ :use_tray => tool.editor_button(:use_tray) == "true",
+ :description => if tool.description
+ Sanitize.clean(markdown.render(tool.description), CanvasSanitize::SANITIZE)
+ else
+ ""
+ end
}
end
end
diff --git a/docker-compose.override.yml b/docker-compose.override.yml
index 2f5c55c0d3e..20c7c7b79ca 100644
--- a/docker-compose.override.yml
+++ b/docker-compose.override.yml
@@ -43,6 +43,7 @@ services:
environment:
<<: *BASE-ENV
VIRTUAL_HOST: .canvas.docker
+ HTTPS_METHOD: noredirect
postgres:
volumes:
diff --git a/packages/canvas-rce/package.json b/packages/canvas-rce/package.json
index 4de75e525d5..7932dfb9227 100644
--- a/packages/canvas-rce/package.json
+++ b/packages/canvas-rce/package.json
@@ -9,7 +9,7 @@
"integration-test": "nightwatch --env integration",
"lint": "eslint \"src/**/*.js\" \"test/**/*.js\"",
"lint:fix": "eslint --fix \"src/**/*.js\" \"test/**/*.js\"",
- "test": "Test cafe will be added back to test as part of CORE-2995",
+ "_test": "Test cafe will be added back to test as part of CORE-2995",
"test": "yarn test:mocha && yarn test:jest",
"test:mocha": "BABEL_ENV=test-node mocha 'test/**/*.test.js' --require @instructure/canvas-theme --require @babel/register --timeout 5000 --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter-config.json",
"test:mocha:one": "BABEL_ENV=test-node mocha --require @instructure/canvas-theme --require @babel/register --timeout 5000 --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter-config.json",
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/clickCallback.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/clickCallback.js
new file mode 100644
index 00000000000..35b8c57a469
--- /dev/null
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/clickCallback.js
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 - present Instructure, Inc.
+ *
+ * This file is part of Canvas.
+ *
+ * Canvas is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the Free
+ * Software Foundation, version 3 of the License.
+ *
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along
+ * with this program. If not, see .
+ */
+
+import React from 'react'
+import ReactDOM from 'react-dom'
+
+export default function clickCallback(ed, ltiButtons) {
+
+ return import('./components/LtiToolsModal').then(({LtiToolsModal}) => {
+ let container = document.querySelector('.canvas-rce-upload-container')
+ if (!container) {
+ container = document.createElement('div')
+ container.className = 'canvas-rce-upload-container'
+ document.body.appendChild(container)
+ }
+
+ const handleDismiss = () => {
+ ReactDOM.unmountComponentAtNode(container)
+ ed.focus()
+ }
+
+ ReactDOM.render(
+ , container
+ )
+ })
+}
\ No newline at end of file
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/ExpandoText.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/ExpandoText.js
new file mode 100644
index 00000000000..90367798db8
--- /dev/null
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/ExpandoText.js
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 - present Instructure, Inc.
+ *
+ * This file is part of Canvas.
+ *
+ * Canvas is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the Free
+ * Software Foundation, version 3 of the License.
+ *
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along
+ * with this program. If not, see .
+ */
+
+import React, {useState} from 'react'
+import {string} from 'prop-types'
+import { StyleSheet, css } from "aphrodite";
+
+import {Text} from '@instructure/ui-elements'
+import {View} from '@instructure/ui-layout'
+import {IconArrowOpenDownLine, IconArrowOpenEndLine} from '@instructure/ui-icons'
+
+export default function ExpandoText(props) {
+ const [descExpanded, setDescExpanded] = useState(false)
+ const [focused, setFocused] = useState(false)
+
+ const {text} = props
+ return (
+ {
+ if(event.target.tagName !== 'A' || event.target.tagName !== 'BUTTON') {
+ // let the user click on links and buttons
+ setDescExpanded(!descExpanded)
+ }
+ }}
+ onFocus={() => setFocused(true)}
+ onBlur={() => setFocused(false)}
+ >
+
+
+
+ {descExpanded ? : }
+
+
+
+
+
+
+
+
+
+ )
+}
+
+ExpandoText.propTypes = {
+ text: string.isRequired
+}
+
+export const styles = StyleSheet.create({
+ toggleButton: {
+ background: 'transparent',
+ borderStyle: 'none',
+ display: 'block',
+ padding: '.25rem',
+ textAlign: 'start',
+ maxWidth: '100%'
+ },
+ descriptionText: {
+ overflow: 'hidden',
+ lineHeight: '1.2rem',
+ p: {
+ margin: '1rem 0'
+ },
+ ':nth-child(1n)> :first-child': {
+ marginTop: '0',
+ display: 'inline-block'
+ },
+ ':nth-child(1n)> :last-child': {
+ marginBottom: '0'
+ }
+ },
+ overflow: {
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ height: '1.2rem',
+ textOverflow: 'ellipsis'
+ }
+});
\ No newline at end of file
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/LtiTool.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/LtiTool.js
new file mode 100644
index 00000000000..519f622db75
--- /dev/null
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/LtiTool.js
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 - present Instructure, Inc.
+ *
+ * This file is part of Canvas.
+ *
+ * Canvas is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the Free
+ * Software Foundation, version 3 of the License.
+ *
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along
+ * with this program. If not, see .
+ */
+
+import React, {useState} from 'react'
+import {func, string} from 'prop-types'
+import {Text} from '@instructure/ui-elements'
+import {View} from '@instructure/ui-layout'
+import ExpandoText from './ExpandoText'
+
+
+export default function LtiTool(props) {
+ const [focused, setFocused] = useState(false)
+ const {title, image, description, onAction} = props
+
+ return (
+ <>
+ {
+ onAction()
+ }}
+ onKeyDown={(e) => {
+ if (e.keyCode === 13 || e.keyCode === 32) {
+ onAction()
+ }
+ }}
+ onFocus={() => setFocused(true)}
+ onBlur={() => setFocused(false)}
+ tabIndex="0"
+ >
+
+ {title}
+
+ {description && renderDescription(description)}
+ >
+ )
+
+ function renderDescription(desc) {
+ return (
+
+
+
+ )
+ }
+}
+
+LtiTool.propTypes = {
+ title: String.isRequired,
+ image: string.isRequired,
+ onAction: func.isRequired,
+ description: string
+}
\ No newline at end of file
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/ExpandoText.test.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/ExpandoText.test.js
new file mode 100644
index 00000000000..3705516375d
--- /dev/null
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/ExpandoText.test.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 - present Instructure, Inc.
+ *
+ * This file is part of Canvas.
+ *
+ * Canvas is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the Free
+ * Software Foundation, version 3 of the License.
+ *
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along
+ * with this program. If not, see .
+ */
+
+import React from 'react'
+import {render, fireEvent} from '@testing-library/react'
+
+import ExpandoText from '../ExpandoText'
+
+describe('RCE Plugins > LtiTool', () => {
+
+ function renderComponent(text) {
+ return render(
+
+ )
+
+ )
+ }
+
+ it('renters right-arrow when first rendered', () => {
+ const {container} = renderComponent('hello world')
+ expect(container.querySelector('svg[name="IconArrowOpenEnd"]')).toBeInTheDocument()
+ })
+
+ it('renders the text', () => {
+ const {getByText} = renderComponent('hello world')
+ expect(getByText("hello world")).toBeInTheDocument()
+ })
+
+ it('renders the down-arrow when expanded', () => {
+ const {container} = renderComponent('hello world')
+ const arrowButton = container.querySelector('svg[name="IconArrowOpenEnd"]')
+ fireEvent.click(arrowButton)
+ expect(container.querySelector('svg[name="IconArrowOpenDown"]')).toBeInTheDocument()
+ })
+
+ it('renders the right-arrow when collapsed', () => {
+ const {container} = renderComponent('hello world')
+ fireEvent.click(container.querySelector('svg[name="IconArrowOpenEnd"]'))
+ const downButton = container.querySelector('svg[name="IconArrowOpenDown"]')
+ expect(downButton).toBeInTheDocument()
+ fireEvent.click(downButton)
+ expect(container.querySelector('svg[name="IconArrowOpenEnd"]')).toBeInTheDocument()
+ })
+})
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/LtiTool.test.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/LtiTool.test.js
new file mode 100644
index 00000000000..2c59256024b
--- /dev/null
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/LtiTool.test.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 - present Instructure, Inc.
+ *
+ * This file is part of Canvas.
+ *
+ * Canvas is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the Free
+ * Software Foundation, version 3 of the License.
+ *
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along
+ * with this program. If not, see .
+ */
+
+import React from 'react'
+import {render} from '@testing-library/react'
+
+import LtiTool from '../LtiTool'
+
+describe('RCE Plugins > LtiTool', () => {
+
+ function getProps(override={}) {
+ const props = {
+ title: "Tool 1",
+ id: 1,
+ description: "This is tool 1.",
+ image: "tool1/icon.png",
+ onAction: () => {},
+ ...override
+ }
+ return props
+ }
+
+ function renderComponent(toolprops) {
+ return render()
+ }
+
+ it('renters the tool title', () => {
+ const {getByText} = renderComponent()
+ expect(getByText("Tool 1")).toBeInTheDocument()
+ })
+
+ it('renters the tool image', () => {
+ const {container} = renderComponent()
+ expect(container.querySelector('img[src="tool1/icon.png"]')).toBeInTheDocument()
+ })
+
+ it('renders the tool description', () => {
+ const {getByText} = renderComponent()
+ expect(getByText("This is tool 1.")).toBeInTheDocument()
+ })
+})
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/LtiToolsModal.test.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/LtiToolsModal.test.js
new file mode 100644
index 00000000000..846d17b2454
--- /dev/null
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/__tests__/LtiToolsModal.test.js
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 - present Instructure, Inc.
+ *
+ * This file is part of Canvas.
+ *
+ * Canvas is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the Free
+ * Software Foundation, version 3 of the License.
+ *
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along
+ * with this program. If not, see .
+ */
+
+import React from 'react'
+import {render} from '@testing-library/react'
+
+import {LtiToolsModal} from '../index'
+
+describe('RCE Plugins > LtiToolModal', () => {
+
+ function getProps(override={}) {
+ const props = {
+ onDismiss: () => {},
+ ltiButtons: [
+ {
+ title: "Tool 1",
+ id: 1,
+ description: "This is tool 1.",
+ image: "tool1/icon.png",
+ onAction: () => {}
+ },
+ {
+ title: "Tool 2",
+ id: 2,
+ description: "This is tool 2",
+ image: "/tool2/image.png",
+ onAction: () => {}
+ },
+ {
+ title: "Tool 3",
+ id: 3,
+ image: "https://www.edu-apps.org/assets/lti_public_resources/tool3.png",
+ onAction: () => {}
+ }
+ ],
+ ...override
+ }
+ return props
+ }
+
+ function renderComponent(modalprops) {
+ return render()
+ }
+
+ it('is labeled "Select App"', () => {
+ const {getByLabelText} = renderComponent()
+ expect(getByLabelText("LTI Tools")).toBeInTheDocument()
+ })
+
+ it('has heading "Select App"', () => {
+ const {getByText} = renderComponent()
+ expect(getByText("Select App")).toBeInTheDocument()
+ })
+
+ it('shows the 3 tools', () => {
+ const {baseElement, getByText} = renderComponent()
+ expect(getByText('Tool 1')).toBeInTheDocument()
+ expect(getByText('This is tool 1.')).toBeInTheDocument()
+ expect(baseElement.querySelector('img[src="tool1/icon.png"]')).toBeInTheDocument()
+ expect(getByText('Tool 2')).toBeInTheDocument()
+ expect(getByText('This is tool 2')).toBeInTheDocument()
+ expect(baseElement.querySelector('img[src="/tool2/image.png"]')).toBeInTheDocument()
+ expect(getByText('Tool 3')).toBeInTheDocument()
+ expect(baseElement.querySelector('img[src="https://www.edu-apps.org/assets/lti_public_resources/tool3.png"]')).toBeInTheDocument()
+ })
+
+ it('calls onDismiss when clicking Cancel', () => {
+ const handleDismiss = jest.fn()
+ const {getByText} = renderComponent({onDismiss: handleDismiss})
+ const cancelButton = getByText('Cancel')
+ cancelButton.click()
+ expect(handleDismiss).toHaveBeenCalled()
+ })
+
+ it('calls onDismiss when clicking the close button', () => {
+ const handleDismiss = jest.fn()
+ const {getByText} = renderComponent({onDismiss: handleDismiss})
+ const closeButton = getByText('Close')
+ closeButton.click()
+ expect(handleDismiss).toHaveBeenCalled()
+ })
+
+ it('calls onAction when clicking a tool', () => {
+ const doAction = jest.fn()
+ const onDismiss = jest.fn()
+ const {getByText} = renderComponent({
+ onDismiss,
+ ltiButtons: [
+ {
+ title: "Tool 1",
+ id: 1,
+ description: "This is tool 1.",
+ image: "tool1/icon.png",
+ onAction: doAction
+ }
+ ]})
+ const tool1 = getByText('Tool 1')
+ tool1.click()
+ expect(doAction).toHaveBeenCalled()
+ expect(onDismiss).toHaveBeenCalled()
+ })
+})
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/index.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/index.js
new file mode 100644
index 00000000000..4c20bd90739
--- /dev/null
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/components/LtiToolsModal/index.js
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 - present Instructure, Inc.
+ *
+ * This file is part of Canvas.
+ *
+ * Canvas is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the Free
+ * Software Foundation, version 3 of the License.
+ *
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along
+ * with this program. If not, see .
+ */
+
+import React from 'react'
+import {func, arrayOf, oneOfType, number, shape, string} from 'prop-types'
+import {Modal} from '@instructure/ui-overlays'
+import {Button, CloseButton} from '@instructure/ui-buttons'
+import {Heading, List} from '@instructure/ui-elements'
+import {View} from '@instructure/ui-layout'
+import formatMessage from '../../../../../format-message'
+import LtiTool from './LtiTool'
+
+// TODO: we really need a way for the client to pass this to the RCE
+const getLiveRegion=() => document.getElementById('flash_screenreader_holder')
+
+export function LtiToolsModal(props) {
+ return (
+
+
+
+ {formatMessage('Close')}
+
+ {formatMessage('Select App')}
+
+
+ {renderTools(props.ltiButtons)}
+
+
+
+
+
+ )
+
+ function renderTools(ltiButtons) {
+ return (
+
+ {ltiButtons.sort((a, b) => a.title.localeCompare(b.title)).map((b, i) => {
+ return (
+
+
+ {
+ b.onAction()
+ props.onDismiss()
+ }}
+ description={b.description}
+ />
+
+
+ )
+ })}
+
+ )
+ }
+
+
+}
+
+LtiToolsModal.propTypes = {
+ ltiButtons: arrayOf(shape({
+ description: string.isRequired,
+ id: oneOfType([string, number]).isRequired,
+ image: string.isRequired,
+ onAction: func.isRequired,
+ title: string.isRequired
+ })),
+ onDismiss: func.isRequired
+}
\ No newline at end of file
diff --git a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/plugin.js b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/plugin.js
index aa253000832..5fbc18302d6 100644
--- a/packages/canvas-rce/src/rce/plugins/instructure_external_tools/plugin.js
+++ b/packages/canvas-rce/src/rce/plugins/instructure_external_tools/plugin.js
@@ -18,10 +18,14 @@
import dispatchInitEvent from "./dispatchInitEvent";
import {IconLtiLine} from '@instructure/ui-icons/es/svg'
+import clickCallback from './clickCallback'
tinymce.create("tinymce.plugins.InstructureExternalTools", {
init(ed, url) {
+ document.addEventListener('tinyRCE/onExternalTools', (event) => {
+ clickCallback(ed, event.detail.ltiButtons)
+ })
ed.ui.registry.addIcon('lti', IconLtiLine.src)
dispatchInitEvent(ed, document, url);
},
diff --git a/public/javascripts/tinymce_plugins/instructure_external_tools/ExternalToolsHelper.js b/public/javascripts/tinymce_plugins/instructure_external_tools/ExternalToolsHelper.js
index bb6caf29b74..0b67f77f882 100644
--- a/public/javascripts/tinymce_plugins/instructure_external_tools/ExternalToolsHelper.js
+++ b/public/javascripts/tinymce_plugins/instructure_external_tools/ExternalToolsHelper.js
@@ -54,9 +54,9 @@ export default {
classes: 'widget btn instructure_external_tool_button'
}
if (ENV.use_rce_enhancements) {
- config.text = config.title
+ config.id = button.id
config.onAction = () => editor.execCommand(`instructureExternalButton${button.id}`)
- config.type = 'menuitem'
+ config.description = button.description
} else {
config.cmd = `instructureExternalButton${button.id}`
}
diff --git a/public/javascripts/tinymce_plugins/instructure_external_tools/initializeExternalTools.js b/public/javascripts/tinymce_plugins/instructure_external_tools/initializeExternalTools.js
index 2cc2ee49dec..c08de412e01 100644
--- a/public/javascripts/tinymce_plugins/instructure_external_tools/initializeExternalTools.js
+++ b/public/javascripts/tinymce_plugins/instructure_external_tools/initializeExternalTools.js
@@ -82,9 +82,10 @@ const ExternalToolsPlugin = {
}
}
if (ltiButtons.length && ENV.use_rce_enhancements) {
- ed.ui.registry.addMenuButton('lti_tool_dropdown', {
- fetch(callback) {
- callback(ltiButtons)
+ ed.ui.registry.addButton('lti_tool_dropdown', {
+ onAction: () => {
+ const ev = new CustomEvent('tinyRCE/onExternalTools', {detail: {ltiButtons}})
+ document.dispatchEvent(ev)
},
icon: 'lti',
tooltip: 'LTI Tools'
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 2401fa69979..456ec076ca0 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -516,12 +516,28 @@ describe ApplicationHelper do
it "should return hash of tools if in group" do
@course = course_model
@group = @course.groups.create!(:name => "some group")
- tool = @course.context_external_tools.new(:name => "bob", :consumer_key => "test", :shared_secret => "secret", :url => "http://example.com")
+ tool = @course.context_external_tools.new(
+ :name => "bob",
+ :consumer_key => "test",
+ :shared_secret => "secret",
+ :url => "http://example.com",
+ :description => "the description."
+ )
tool.editor_button = {:url => "http://example.com", :icon_url => "http://example.com", :canvas_icon_class => 'icon-commons'}
tool.save!
@context = @group
- expect(editor_buttons).to eq([{:name=>"bob", :id=>tool.id, :url=>"http://example.com", :icon_url=>"http://example.com", :canvas_icon_class => 'icon-commons', :width=>800, :height=>400, :use_tray => false}])
+ expect(editor_buttons).to eq([{
+ :name=>"bob",
+ :id=>tool.id,
+ :url=>"http://example.com",
+ :icon_url=>"http://example.com",
+ :canvas_icon_class => 'icon-commons',
+ :width=>800,
+ :height=>400,
+ :use_tray => false,
+ :description => "
the description.
\n"
+ }])
end
it "should return hash of tools if in course" do
@@ -532,7 +548,17 @@ describe ApplicationHelper do
allow(controller).to receive(:group_external_tool_path).and_return('http://dummy')
@context = @course
- expect(editor_buttons).to eq([{:name=>"bob", :id=>tool.id, :url=>"http://example.com", :icon_url=>"http://example.com", :canvas_icon_class => 'icon-commons', :width=>800, :height=>400, :use_tray => false}])
+ expect(editor_buttons).to eq([{
+ :name=>"bob",
+ :id=>tool.id,
+ :url=>"http://example.com",
+ :icon_url=>"http://example.com",
+ :canvas_icon_class => 'icon-commons',
+ :width=>800,
+ :height=>400,
+ :use_tray => false,
+ :description => ""
+ }])
end
it "should not include tools from the domain_root_account for users" do
diff --git a/spec/models/context_external_tool_spec.rb b/spec/models/context_external_tool_spec.rb
index a64e3bb5c91..15cbaf05651 100644
--- a/spec/models/context_external_tool_spec.rb
+++ b/spec/models/context_external_tool_spec.rb
@@ -1624,5 +1624,21 @@ describe ContextExternalTool do
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
expect(json[0][:use_tray]).to eq true
end
+
+ describe 'includes the description' do
+ it 'parsed into HTML' do
+ tool.editor_button = {}
+ tool.description = "the first paragraph.\n\nthe second paragraph."
+ json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
+ expect(json[0][:description]).to eq "
the first paragraph.
\n\n
the second paragraph.
\n"
+ end
+
+ it 'with target="_blank" on links' do
+ tool.editor_button = {}
+ tool.description = "[link text](http://the.url)"
+ json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
+ expect(json[0][:description]).to eq "