Handle content item for default tools
Closes PLAT-4717 Test Plan: - Configure a default external tool for the account - Create an assignment in a course of that account - Verify the tool can return a content item back to Canvas - Verify the UI changes to reflect that content was retrieved - Verify saving the assignment launches to the URL sent in the content item Change-Id: I97fb34346ba49ad58bdafa972f6c3537b665f26d Reviewed-on: https://gerrit.instructure.com/204525 Tested-by: Jenkins Reviewed-by: Marc Phillips <mphillips@instructure.com> QA-Review: Tucker Mcknight <tmcknight@instructure.com> Product-Review: Weston Dransfield <wdransfield@instructure.com>
This commit is contained in:
parent
d766d16326
commit
9d29ecc713
|
@ -40,7 +40,7 @@ import deparam from '../../util/deparam'
|
|||
import SisValidationHelper from '../../util/SisValidationHelper'
|
||||
import SimilarityDetectionTools from 'jsx/assignments/AssignmentConfigurationTools'
|
||||
import ModeratedGradingFormFieldGroup from 'jsx/assignments/ModeratedGradingFormFieldGroup'
|
||||
import DefaultToolForm from 'jsx/assignments/DefaultToolForm'
|
||||
import DefaultToolForm, { toolSubmissionType } from 'jsx/assignments/DefaultToolForm'
|
||||
import AssignmentExternalTools from 'jsx/assignments/AssignmentExternalTools'
|
||||
import * as returnToHelper from '../../../jsx/shared/helpers/returnToHelper'
|
||||
import 'jqueryui/dialog'
|
||||
|
@ -495,6 +495,9 @@ export default class EditView extends ValidatedFormView
|
|||
|
||||
@cacheAssignmentSettings()
|
||||
|
||||
# Get the submission type if an alias type is used (e.g. default_external_tool)
|
||||
$(SUBMISSION_TYPE).val(toolSubmissionType($(SUBMISSION_TYPE).val()))
|
||||
|
||||
if @dueDateOverrideView.containsSectionsWithoutOverrides()
|
||||
sections = @dueDateOverrideView.sectionsWithoutOverrides()
|
||||
missingDateDialog = new MissingDateDialog
|
||||
|
|
|
@ -19,17 +19,25 @@
|
|||
import $ from 'jquery'
|
||||
import axios from 'axios'
|
||||
import I18n from 'i18n!DefaultToolForm'
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {useState, useEffect} from 'react'
|
||||
|
||||
import SelectContentDialog from '../../../public/javascripts/select_content_dialog.js'
|
||||
import usePostMessage from './hooks/usePostMessage'
|
||||
|
||||
import {Alert} from '@instructure/ui-alerts'
|
||||
import {Button} from '@instructure/ui-buttons'
|
||||
import {View} from '@instructure/ui-layout'
|
||||
import { Alert } from '@instructure/ui-alerts'
|
||||
import { Button } from '@instructure/ui-buttons'
|
||||
import { Text } from '@instructure/ui-elements'
|
||||
import { View } from '@instructure/ui-layout'
|
||||
|
||||
export function toolSubmissionType(submissionType) {
|
||||
const toolTypes = ['default_external_tool']
|
||||
return toolTypes.includes(submissionType) ? 'external_tool' : submissionType
|
||||
}
|
||||
|
||||
const DefaultToolForm = props => {
|
||||
const [launchDefinitions, setLaunchDefinitions] = useState([])
|
||||
const toolMessageData = usePostMessage('defaultToolContentReady')
|
||||
|
||||
const defaultToolData = launchDefinitions.find(definition =>
|
||||
Object.values(definition.placements).find(placement => placement.url === props.toolUrl)
|
||||
|
@ -59,9 +67,18 @@ const DefaultToolForm = props => {
|
|||
<Button id="default-tool-launch-button" onClick={handleLaunchButton}>
|
||||
{I18n.t('Add a Question Set')}
|
||||
</Button>
|
||||
<Alert variant="info" renderCloseButtonLabel="Close" margin="small small 0 0">
|
||||
{I18n.t('Click the button above to add a WileyPLUS Question Set')}
|
||||
</Alert>
|
||||
|
||||
{toolMessageData ? (
|
||||
<Alert variant="success" margin="small small 0 0">
|
||||
<Text weight="bold">{toolMessageData.content.title}</Text><br/>
|
||||
<Text>{I18n.t('Successfully Added')}</Text>
|
||||
</Alert>
|
||||
) : (
|
||||
<Alert variant="info" margin="small small 0 0">
|
||||
{I18n.t('Click the button above to add a WileyPLUS Question Set')}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{defaultToolData && (
|
||||
<div style={{display: 'none'}}>
|
||||
<ul className="tools">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2018 - present Instructure, Inc.
|
||||
* Copyright (C) 2019 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
|
@ -18,7 +18,7 @@
|
|||
|
||||
import React from 'react'
|
||||
import {mount} from 'enzyme'
|
||||
import DefaultToolForm from '../DefaultToolForm'
|
||||
import DefaultToolForm, { toolSubmissionType } from '../DefaultToolForm'
|
||||
import SelectContentDialog from '../../../../public/javascripts/select_content_dialog.js'
|
||||
|
||||
const newProps = (overrides = {}) => ({
|
||||
|
@ -29,26 +29,38 @@ const newProps = (overrides = {}) => ({
|
|||
...overrides
|
||||
})
|
||||
|
||||
let wrapper = 'empty wrapper'
|
||||
describe('DefaultToolForm', () => {
|
||||
let wrapper = 'empty wrapper'
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount()
|
||||
afterEach(() => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('renders a button to launch the tool', () => {
|
||||
wrapper = mount(<DefaultToolForm {...newProps()} />)
|
||||
expect(wrapper.find('#default-tool-launch-button')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('launches the tool when the button is clicked', () => {
|
||||
SelectContentDialog.Events.onContextExternalToolSelect = jest.fn()
|
||||
wrapper = mount(<DefaultToolForm {...newProps()} />)
|
||||
wrapper.find('#default-tool-launch-button').first().simulate('click')
|
||||
expect(SelectContentDialog.Events.onContextExternalToolSelect).toHaveBeenCalled()
|
||||
SelectContentDialog.Events.onContextExternalToolSelect.mockRestore()
|
||||
})
|
||||
|
||||
it('renders the information mesage', () => {
|
||||
wrapper = mount(<DefaultToolForm {...newProps()} />)
|
||||
expect(wrapper.find('Alert').html()).toContain('Click the button above to add a WileyPLUS Question Set')
|
||||
})
|
||||
})
|
||||
|
||||
it('renders a button to launch the tool', () => {
|
||||
wrapper = mount(<DefaultToolForm {...newProps()} />)
|
||||
expect(wrapper.find('#default-tool-launch-button')).toBeTruthy()
|
||||
})
|
||||
describe('toolSubmissionType', () => {
|
||||
it('returns "external_tool" if the submission type is "default_external_tool"', () => {
|
||||
expect(toolSubmissionType('default_external_tool')).toEqual('external_tool')
|
||||
})
|
||||
|
||||
it('launches the tool when the button is clicked', () => {
|
||||
SelectContentDialog.Events.onContextExternalToolSelect = jest.fn()
|
||||
wrapper = mount(<DefaultToolForm {...newProps()} />)
|
||||
wrapper.find('#default-tool-launch-button').first().simulate('click')
|
||||
expect(SelectContentDialog.Events.onContextExternalToolSelect).toHaveBeenCalled()
|
||||
SelectContentDialog.Events.onContextExternalToolSelect.mockRestore()
|
||||
})
|
||||
|
||||
it('renders the information mesage', () => {
|
||||
wrapper = mount(<DefaultToolForm {...newProps()} />)
|
||||
expect(wrapper.find('Alert').html()).toContain('Click the button above to add a WileyPLUS Question Set')
|
||||
it('returns the submission type if it is not a tool submission type', () => {
|
||||
expect(toolSubmissionType('online')).toEqual('online')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, {useState, useEffect} from 'react'
|
||||
|
||||
export default function usePostMessage(expectedMessageType) {
|
||||
const [messageData, setMessageData] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
function handlePostMessage(postMessage) {
|
||||
const {messageType} = postMessage.data
|
||||
|
||||
if (
|
||||
postMessage.origin === ENV.DEEP_LINKING_POST_MESSAGE_ORIGIN &&
|
||||
messageType === expectedMessageType
|
||||
) {
|
||||
setMessageData(postMessage.data)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', handlePostMessage, false)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', handlePostMessage)
|
||||
}
|
||||
})
|
||||
|
||||
return messageData
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import setDefaultToolValues from '../setDefaultToolValues'
|
||||
|
||||
describe('setDefaultToolValues', () => {
|
||||
|
||||
const definition_type = "ContextExternalTool"
|
||||
const definition_id = '22'
|
||||
const tool = {
|
||||
definition_type,
|
||||
definition_id
|
||||
}
|
||||
|
||||
const url = 'https://www.test-tool.com/lti_launch'
|
||||
const result = {
|
||||
url
|
||||
}
|
||||
|
||||
const postMessageOrigin = window.ENV.DEEP_LINKING_POST_MESSAGE_ORIGIN
|
||||
|
||||
beforeAll(() => {
|
||||
window.ENV.DEEP_LINKING_POST_MESSAGE_ORIGIN = 'canvas.instructure.com'
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML =
|
||||
'<input id="assignment_external_tool_tag_attributes_content_type" type="hidden"/>' +
|
||||
'<input id="assignment_external_tool_tag_attributes_content_id" type="hidden"/>' +
|
||||
'<input id="assignment_external_tool_tag_attributes_url" type="hidden"/>'
|
||||
|
||||
window.postMessage = jest.fn()
|
||||
|
||||
setDefaultToolValues(result, tool)
|
||||
})
|
||||
|
||||
afterEach(() => { window.postMessage.mockReset() })
|
||||
|
||||
afterAll(() => {
|
||||
window.ENV.DEEP_LINKING_POST_MESSAGE_ORIGIN = postMessageOrigin
|
||||
window.postMessage.mockRestore()
|
||||
})
|
||||
|
||||
it('sends a postMessage to the window with results', () => {
|
||||
expect(window.postMessage).toHaveBeenCalledWith({
|
||||
messageType: 'defaultToolContentReady',
|
||||
content: result
|
||||
}, 'canvas.instructure.com')
|
||||
})
|
||||
|
||||
it('sets the definition type', () => {
|
||||
expect(document.querySelector('#assignment_external_tool_tag_attributes_content_type').value)
|
||||
.toEqual(definition_type)
|
||||
})
|
||||
|
||||
it('sets the definition id', () => {
|
||||
expect(document.querySelector('#assignment_external_tool_tag_attributes_content_id').value)
|
||||
.toEqual(definition_id)
|
||||
})
|
||||
|
||||
it('sets the tool URL', () => {
|
||||
expect(document.querySelector('#assignment_external_tool_tag_attributes_url').value)
|
||||
.toEqual(url)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import $ from 'jquery'
|
||||
|
||||
export default function setDefaultToolValues(result, tool) {
|
||||
$('#assignment_external_tool_tag_attributes_content_type').val(tool.definition_type)
|
||||
$('#assignment_external_tool_tag_attributes_content_id').val(tool.definition_id)
|
||||
$('#assignment_external_tool_tag_attributes_url').val(result.url)
|
||||
|
||||
window.postMessage({
|
||||
messageType: 'defaultToolContentReady',
|
||||
content: result
|
||||
}, ENV.DEEP_LINKING_POST_MESSAGE_ORIGIN)
|
||||
}
|
|
@ -27,6 +27,7 @@ import htmlEscape from 'str/htmlEscape'
|
|||
import { uploadFile } from 'jsx/shared/upload_file'
|
||||
import iframeAllowances from 'jsx/external_apps/lib/iframeAllowances'
|
||||
import SelectContent from './lti/select_content'
|
||||
import setDefaultToolValues from './lti/setDefaultToolValues'
|
||||
import processSingleContentItem from 'jsx/deep_linking/processors/processSingleContentItem'
|
||||
import {findLinkForService, getUserServices} from './findLinkForService'
|
||||
import './jquery.instructure_date_and_time' /* datetime_field */
|
||||
|
@ -94,6 +95,9 @@ import './jquery.templateData'
|
|||
}
|
||||
|
||||
SelectContentDialog.handleContentItemResult = function(result, tool) {
|
||||
if (ENV.DEFAULT_ASSIGNMENT_TOOL_NAME && ENV.DEFAULT_ASSIGNMENT_TOOL_URL) {
|
||||
setDefaultToolValues(result, tool)
|
||||
}
|
||||
$("#external_tool_create_url").val(result.url)
|
||||
$("#external_tool_create_title").val(result.title || tool.name)
|
||||
$("#context_external_tools_select .domain_message").hide()
|
||||
|
|
Loading…
Reference in New Issue