Implement New Files Preview

This is the first commit of preview functionality into New Files
it isn't polished, but should be a good starting point for
polish.  Tickets are being created to account for any remaining
tasks.

closes CNVS-14727

Test Plan: (sorry kinda big)
  - Enable "Better File Browsing" for a course
  - Go to course files (/courses/##/files)
  - Make sure you have some images uploaded... since that's all
    that is really supported.
  - Click on a file (but not on the file name itself)
  - It should highlight
  - Click the "View" option in the toolbar that should appear
  - An overlay should cover the screen
  - You should be able to tab to each of the interactive
    elements on this page
  - Clicking Download should download the file.
  - Clicking Info should open the information panel.
  - Clicking the show button at the bottom should open up the
    preview thumbnails
  - The arrow keys (and the buttons on the screeen) should
    navigate left/right
  - A non-image file should show you a nice message telling
    you it can't be previewed yet.
  - Clicking close should, you know... close the panel.
  - Now select multiple files and click "view" again.
  - Perform all the same tests, but now you should be limited
    to only the items that you selected.

Change-Id: I8f48a133f739b9bddda5e340f5156269691c7870
Reviewed-on: https://gerrit.instructure.com/41451
QA-Review: Jahnavi Yetukuri <jyetukuri@instructure.com>
Reviewed-by: Ryan Shaw <ryan@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
Product-Review: Cosme Salazar <cosme@instructure.com>
This commit is contained in:
Clay Diffrient 2014-09-30 13:51:31 -06:00
parent df6d8b969b
commit 9a4f37a4bc
9 changed files with 670 additions and 20 deletions

View File

@ -0,0 +1,230 @@
define [
'underscore',
'react'
'react-router',
'bower/react-modal/dist/react-modal'
'i18n!file_preview'
'./FriendlyDatetime'
'compiled/models/Folder',
'compiled/react/shared/utils/withReactDOM'
'../utils/collectionHandler'
], (_, React, ReactRouter, ReactModal, I18n, FriendlyDatetime, Folder, withReactDOM, collectionHandler) ->
FilePreview = React.createClass
displayName: 'FilePreview'
mixins: [React.addons.PureRenderMixin]
propTypes:
initialItem: React.PropTypes.object
otherItems: React.PropTypes.array
otherItemsString: React.PropTypes.string
getInitialState: ->
showInfoPanel: false
showFooter: false
showFooterBtn: true
displayedItem: @props.initialItem
user: @props.initialItem.get 'user'
otherItemsIsBackBoneCollection: @props.otherItems instanceof Backbone.Collection
componentWillMount: ->
ReactModal.setAppElement(@props.appElement)
componentDidMount: ->
$('.ReactModal__Overlay').on 'keydown', @handleKeyboardNavigation
componentWillUnmount: ->
$('.ReactModal__Overlay').off 'keydown', @handleKeyboardNavigation
componentWillReceiveProps: (newProps) ->
@setState(
displayedItem: newProps.initialItem
user: newProps.initialItem.get 'user'
otherItemPreviewString: @setUpOtherItemsQuery(newProps.otherItems)
)
setUpOtherItemsQuery: (otherItems) ->
otherItems.map((item) ->
item.id
).join(',')
openInfoPanel: (event) ->
event.preventDefault()
@setState({showInfoPanel: !@state.showInfoPanel});
toggleFooter: (event) ->
event.preventDefault()
@setState({showFooter: !@state.showFooter});
handleKeyboardNavigation: (event) ->
return null unless (event.keyCode is 37 or event.keyCode is 39)
# left arrow
if (event.keyCode is 37)
nextItem = collectionHandler.getPreviousInRelationTo(@props.otherItems, @state.displayedItem)
# right arrow
if (event.keyCode is 39)
nextItem = collectionHandler.getNextInRelationTo(@props.otherItems, @state.displayedItem)
if (@props.otherItemsString)
ReactRouter.transitionTo((if @props.params.splat then 'folder' else 'rootFolder'), @props.params, {preview: nextItem.id, only_preview: @props.otherItemsString})
else
ReactRouter.transitionTo((if @props.params.splat then 'folder' else 'rootFolder'), @props.params, {preview: nextItem.id})
getStatusMessage: ->
'A nice status message ;) ' #TODO: Actually do this..
renderPreview: ->
fileNameParts = @state.displayedItem.displayName().split('.')
fileExt = fileNameParts[fileNameParts.length - 1].toUpperCase()
contentType = @state.displayedItem.get('content-type')
div {className: if @state.showInfoPanel then 'ef-file-preview-item full-height col-xs-6' else 'ef-file-preview-item full-height col-xs-10'},
if contentType.substring(0, contentType.indexOf('/')) is 'image'
img {className: 'ef-file-preview-image' ,src: @state.displayedItem.get('url')}
else
h1 {className: 'ef-file-preview-not-available'},
"Previewing a #{fileExt} file is not yet available."
renderArrowLink: (direction) ->
# TODO: Refactor this to use the collectionHandler
# Get the current position in the collection
curItemIndex = @props.otherItems.indexOf(@state.displayedItem)
switch direction
when 'left'
goToItemIndex = curItemIndex - 1;
if goToItemIndex < 0
goToItemIndex = @props.otherItems.length - 1
when 'right'
goToItemIndex = curItemIndex + 1;
if goToItemIndex > @props.otherItems.length - 1
goToItemIndex = 0
goToItem = if @state.otherItemsIsBackBoneCollection then @props.otherItems.at(goToItemIndex) else @props.otherItems[goToItemIndex]
if (@props.otherItemsString)
@props.params.only_preview = @props.otherItemsString
switch direction
when 'left'
div {className: 'col-xs-1 full-height'},
ReactRouter.Link _.defaults({to: (if @props.params.splat then 'folder' else 'rootFolder'), query: {preview: goToItem.id}, className: 'ef-file-preview-arrow-link'}, @props.params),
div {className: 'ef-file-preview-arrow'},
i {className: 'icon-arrow-open-left'}
when 'right'
div {className: 'col-xs-1 full-height'},
ReactRouter.Link _.defaults({to: (if @props.params.splat then 'folder' else 'rootFolder'), query: {preview: goToItem.id}, className: 'ef-file-preview-arrow-link'}, @props.params),
div {className: 'ef-file-preview-arrow'},
i {className: 'icon-arrow-open-right'}
scrollLeft: (event) ->
width = $('.ef-file-preview-footer-list').width();
console.log("left scroll");
$('.ef-file-preview-footer-list').animate({
scrollLeft: '-=' + width
}, 300, 'easeOutQuad')
scrollRight: (event) ->
width = $('.ef-file-preview-footer-list').width();
console.log("right scroll");
$('.ef-file-preview-footer-list').animate({
scrollLeft: '+=' + width
}, 300, 'easeOutQuad')
closeModal: ->
ReactRouter.transitionTo((if @props.params.splat then 'folder' else 'rootFolder'), @props.params)
render: withReactDOM ->
ReactModal {isOpen: true, onRequestClose: @closeModal, closeTimeoutMS: 10},
div {className: 'ef-file-preview-overlay'},
div {className: 'ef-file-preview-container'},
div {className: 'ef-file-preview-header grid-row middle-xs'},
div {className: 'col-xs'},
div {className: 'ef-file-preview-header-filename-container'},
h1 {className: 'ef-file-preview-header-filename'},
@props.initialItem.displayName()
div {className: 'col-xs end-xs'},
div {className: 'ef-file-preview-header-buttons'},
a {className: 'ef-file-preview-header-download ef-file-preview-button', href: @state.displayedItem.get('url')},
i {className: 'icon-download'} #Replace with actual icon
I18n.t('file_preview_headerbutton_download', ' Download')
a {className: 'ef-file-preview-header-info ef-file-preview-button', href: '#', onClick: @openInfoPanel},
i {className: 'icon-info'}
I18n.t('file_preview_headerbutton_info', ' Info')
ReactRouter.Link _.defaults({to: (if @props.params.splat then 'folder' else 'rootFolder'), query: '', className: 'ef-file-preview-header-close ef-file-preview-button'}, @props.params),
i {className: 'icon-end'}
I18n.t('file_preview_headerbutton_close', ' Close')
div {className: 'ef-file-preview-preview grid-row middle-xs'},
# We need to render out the left/right arrows
@renderArrowLink('left') if @props.otherItems.length > 0
@renderPreview()
@renderArrowLink('right') if @props.otherItems.length > 0
if @state.showInfoPanel
div {className: 'col-xs-4 full-height ef-file-preview-information'},
table {className: 'ef-file-preview-infotable'},
tbody {},
tr {},
th {scope: 'row'},
I18n.t('file_preview_infotable_name', 'Name')
td {},
@state.displayedItem.displayName()
tr {},
th {scope: 'row'},
I18n.t('file_preview_infotable_status', 'Status')
td {},
@getStatusMessage();
tr {},
th {scope: 'row'},
I18n.t('file_preview_infotable_kind', 'Kind')
td {},
@state.displayedItem.get 'content-type'
tr {},
th {scope: 'row'},
I18n.t('file_preview_infotable_size', 'Size')
td {},
@state.displayedItem.get('size') + ' Kb'
tr {},
th {scope: 'row'},
I18n.t('file_preview_infotable_datemodified', 'Date Modified')
td {},
FriendlyDatetime datetime: @state.displayedItem.get('updated_at')
tr {},
th {scope: 'row'},
I18n.t('file_preview_infotable_modifiedby', 'Modified By')
td {},
img {className: 'avatar', src: @state.user?.avatar_image_url }
a {href: @state.user?.html_url},
@state.user?.display_name
tr {},
th {scope: 'row'},
I18n.t('file_preview_infotable_datecreated', 'Date Created')
td {},
FriendlyDatetime datetime: @state.displayedItem.get('created_at')
div {className: 'ef-file-preview-toggle-row grid-row middle-xs'},
if @state.showFooterBtn
a {className: 'ef-file-preview-toggle col-xs-1 off-xs-1', href: '#', onClick: @toggleFooter, role: 'button', style: {bottom: '21%'} if @state.showFooter},
if @state.showFooter
I18n.t('file_preview_hide', 'Hide')
else
I18n.t('file_preview_show', 'Show')
if @state.showFooter
div {className: 'ef-file-preview-footer grid-row'},
div {className: 'col-xs-1', onClick: @scrollLeft},
div {className: 'ef-file-preview-footer-arrow'},
i {className: 'icon-arrow-open-left'}
div {className: 'col-xs-10'},
ul {className: 'ef-file-preview-footer-list'},
@props.otherItems.map (file) =>
li {className: 'ef-file-preview-footer-list-item', key: file.id},
figure {className: 'ef-file-preview-footer-item'},
ReactRouter.Link _.defaults({to: (if @props.params.splat then 'folder' else 'rootFolder'), query: {preview: file.id}, className: ''}, @props.params),
div {
className: if file.displayName() is @state.displayedItem.displayName() then 'ef-file-preview-footer-image ef-file-preview-footer-active' else 'ef-file-preview-footer-image'
style: {'background-image': 'url(' + file.get('thumbnail_url') + ')'}
}
figcaption {},
file.displayName()
div {className: 'col-xs-1', onClick: @scrollRight},
div {className: 'ef-file-preview-footer-arrow'},
i {className: 'icon-arrow-open-right'}

View File

@ -11,14 +11,16 @@ define [
'../utils/updateAPIQuerySortParams'
'compiled/models/Folder'
'./CurrentUploads'
'./FilePreview'
'./UploadDropZone'
], (_, React, I18n, withReactDOM, filesEnv, ColumnHeaders, LoadingIndicator, FolderChild, getAllPages, updateAPIQuerySortParams, Folder, CurrentUploads, UploadDropZone) ->
], (_, React, I18n, withReactDOM, filesEnv, ColumnHeaders, LoadingIndicator, FolderChild, getAllPages, updateAPIQuerySortParams, Folder, CurrentUploads, FilePreview, UploadDropZone) ->
LEADING_SLASH_TILL_BUT_NOT_INCLUDING_NEXT_SLASH = /^\/[^\/]*/
ShowFolder = React.createClass
displayName: 'ShowFolder'
debouncedForceUpdate: _.debounce ->
@forceUpdate() if @isMounted()
, 0
@ -66,6 +68,7 @@ define [
setTimeout =>
@props.onResolvePath({currentFolder:undefined, rootTillCurrentFolder:undefined})
componentWillReceiveProps: (newProps) ->
@unregisterListeners()
return unless newProps.currentFolder
@ -99,6 +102,28 @@ define [
LoadingIndicator isLoading: @props.currentFolder.folders.fetchingNextPage || @props.currentFolder.files.fetchingNextPage
# Prepare and render the FilePreview if needed.
# As long as ?preview is present in the url.
if @props.query.preview?
# Sets up our collection that we will be using.
onlyIdsToPreview = @props.query.only_preview?.split(',')
otherItems = if onlyIdsToPreview # expects this to be [1,2,34,9] (ids of files to preview)
@props.currentFolder.files.filter (file) ->
file.id in onlyIdsToPreview
else
@props.currentFolder.files
# If preview contains data (i.e. ?preview=4)
if @props.query.preview
# We go back to the folder to pull this data.
initialItem = @props.currentFolder.files.get(@props.query.preview)
# If preview doesn't contain data (i.e. ?preview)
# we'll just use the first one in our otherItems collection.
else
# Because otherItems may (or may not be) a Backbone collection (FilesCollection) we change up our method.
initialItem = if otherItems instanceof Backbone.Collection then otherItems.first() else _.first(otherItems)
# Makes sure other items has something before sending it to the preview.
if otherItems?.length
if @props.query.only_preview
FilePreview {initialItem: initialItem, otherItems: otherItems, params: @props.params, appElement: document.getElementById('content'), otherItemsString: @props.query.only_preview}
else
FilePreview {initialItem: initialItem, otherItems: otherItems, params: @props.params, appElement: document.getElementById('content')}

View File

@ -1,4 +1,5 @@
define [
'underscore'
'i18n!react_files'
'react'
'react-router'
@ -10,7 +11,7 @@ define [
'./RestrictedDialogForm'
'jquery'
'compiled/jquery.rails_flash_notifications'
], (I18n, React, Router, withReactDOM, UploadButton, openMoveDialog, downloadStuffAsAZip, customPropTypes, RestrictedDialogForm, $) ->
], (_, I18n, React, Router, withReactDOM, UploadButton, openMoveDialog, downloadStuffAsAZip, customPropTypes, RestrictedDialogForm, $) ->
Toolbar = React.createClass
displayName: 'Toolbar'
@ -20,6 +21,7 @@ define [
contextType: customPropTypes.contextType.isRequired
contextId: customPropTypes.contextId.isRequired
onSubmitSearch: (event) ->
event.preventDefault()
query = {search_term: @refs.searchTerm.getDOMNode().value}
@ -50,6 +52,17 @@ define [
$.flashMessage I18n.t('deleted_items_successfully', '%{count} items deleted successfully', {count})
@props.clearSelectedItems()
getPreviewQuery: ->
return unless @props.selectedItems.length
if @props.selectedItems.length is 1
{preview: @props.selectedItems[0].id}
else
{
preview: @props.selectedItems[0].id
only_preview: @props.selectedItems.map((item) -> item.id).join(',')
}
# Function Summary
# Create a blank dialog window via jQuery, then dump the RestrictedDialogForm into that
# dialog window. This allows us to do react things inside of this all ready rendered
@ -96,12 +109,12 @@ define [
div className: "ui-buttonset col-xs #{'screenreader-only' unless showingButtons}",
button {
disabled: !showingButtons
Router.Link {
to: (if @props.currentFolder?.urlPath() then 'folder' else 'rootFolder'),
query: @getPreviewQuery()
splat: @props.currentFolder?.urlPath()
className: 'ui-button btn-view'
onClick: alert.bind(null, 'TODO: handle CNVS-14727 actually implement previewing of files')
title: I18n.t('view', 'View')
'aria-label': I18n.t('view', 'View')
'data-tooltip': ''
},
i className: 'icon-search'

View File

@ -0,0 +1,52 @@
#
# Handles navigation through a collection.
#
define [], ->
CollectionHandler =
isBackboneCollection: (collection) ->
return collection instanceof Backbone.Collection
# Get the previous item in a collection.
getPreviousInRelationTo : (collection, collectionItem) ->
isBackbone = @isBackboneCollection(collection)
itemIndex = collection.indexOf(collectionItem)
# Return null if the item wasn't found.
return null unless itemIndex >= 0
nextIndex = itemIndex - 1
# Return the last item if we were at the first.
if nextIndex < 0
if isBackbone
return collection.at(collection.length - 1)
else
return collection[collection.length - 1]
# Otherwise let's just return the previous item.
if isBackbone then collection.at(nextIndex) else collection[nextIndex]
# Get the next item in a collection.
getNextInRelationTo : (collection, collectionItem) ->
isBackbone = @isBackboneCollection(collection)
itemIndex = collection.indexOf(collectionItem)
# Return null if the item wasn't found.
return null unless itemIndex >= 0
nextIndex = itemIndex + 1
# Return the first item if we were at the last.
if nextIndex > collection.length - 1
if isBackbone
return collection.at(0)
else
return collection[0]
# Otherwise let's just return the next item.
if isBackbone then collection.at(nextIndex) else collection[nextIndex]

View File

@ -329,3 +329,207 @@ i[class*=UploadDropZone__instructions--icon-upload] {
// Hack, Hack, Hack!
// to make sure that there is space for the ItemCog menu to appear below the bottom thing in the list of files
#footer { min-height: 80px }
// Make sure that ReactModal gets above everything else
.ReactModal__Overlay {
z-index: 100;
}
.ef-file-preview-overlay {
position: fixed;
left: 0; top: 0;
width: 100%; height: 100%;
background-color: rgba(0,0,0,0.7);
}
.ef-file-preview-container {
display: flex;
flex-direction: column;
height: 100%
}
.ef-file-preview-header {
// position: relative;
// top: 0;
// left: 0;
// right: 0;
//height: 70px;
flex: 0 0 70px;
background-color: #000;
opacity: 1;
border-bottom: 1px solid #666;
}
.ef-file-preview-preview {
flex: 1;
display: flex;
&.full-height {
flex: 0 0 400px;
}
// height: 100vh;
// position: absolute;
// top: 70px;
// left: 0;
// right: 0;
// height: 100%;
// width: 100%;
}
.ef-file-preview-arrow-container {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
flex: 0 0 100px;
}
.ef-file-preview-arrow-link {
background-color: #000;
border: 1px solid #666;
text-align: center;
height: 100px;
padding-top: 65px;
}
.ef-file-preview-arrow {
// background-color: #000;
// height: 100px;
// width: 50px;
// i {
// margin-top: 45px;
// margin-left: 13px;
// }
}
.ef-file-preview-header-buttons {
padding: 8px 16px;
}
.ef-file-preview-button {
color: rgba($canvas-light, 0.8);
font-weight: bold;
margin-left: 16px;
cursor: pointer;
&:hover, &:focus { color: $canvas-light; }
@if $use_high_contrast == false {
&:hover, &:focus { text-decoration: none; }
}
}
.ef-file-preview-header-filename-container {
padding: 8px 16px;
}
.ef-file-preview-header-filename {
color: $canvas-light;
margin: 0;
}
.ef-file-preview-information {
// width: 46%;
background-color: #333;
align-self: flex-start;
// position: absolute;
// right: 0;
// float: right;
// top: 70px;
// bottom: 0;
}
.ef-file-preview-infotable {
th {
color: gray;
}
td {
color: white;
}
}
.ef-file-preview-toggle {
color: #666;
font-weight: bold;
background-color: #000;
// width: 100px;
// margin-left: 25px;
text-align: center;
border-top: 1px solid #666;
border-left: 1px solid #666;
border-right: 1px solid #666;
// position: absolute;
// bottom: 10px;
}
.ef-file-preview-footer {
// width: 100%;
// height: 20%;
// position: absolute;
// bottom: 0;
background-color: black;
}
.ef-file-preview-footer-item
{
width: 100px;
height: 100px;
// float: right;
color: white;
display: inline-block;
}
.ef-file-preview-image {
width: 500px;
height: auto;
}
.ef-file-preview-item {
//background-color: red;
align-items: center;
}
.ef-file-preview-toggle-row {
flex: 0 0 70px;
}
.ef-file-preview-footer-list {
list-style-type: none;
white-space: nowrap;
overflow-x: auto;
margin: 0;
padding: 0;
width: 100%;
}
.ef-file-preview-footer-list-item {
display: inline;
}
.ef-file-preview-footer-arrow {
margin-top: 35%;
margin-left: auto;
margin-right: auto;
}
.ef-file-preview-footer-active {
border: solid $canvas-primary 2px;
}
.ef-file-preview-footer-image {
width: 100px;
height: 100px;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
}
.ef-file-preview-not-available {
color: #fff;
background-color: #000;
padding: 200px;
}

View File

@ -109,6 +109,9 @@ module Canvas
deps: ['react'],
exports: 'ReactRouter'
},
'bower/react-modal/dist/react-modal': {
deps: ['react']
},
'bower/ember/ember': {
deps: ['jquery', 'handlebars'],
exports: 'Ember'

View File

@ -0,0 +1,115 @@
define [
'react'
'react-router'
'compiled/react_files/components/FilePreview'
'compiled/models/Folder'
'compiled/models/File'
'compiled/collections/FilesCollection'
'compiled/react_files/components/FolderChild'
], (React, Router, FilePreview, Folder, File, FilesCollection, FolderChild) ->
Simulate = React.addons.TestUtils.Simulate
module 'File Preview Rendering',
setup: ->
#window.React = React
sinon.stub(Router, 'Link').returns('some link')
sinon.stub(Folder, 'resolvePath').returns($.Deferred())
# Initialize a few things to view in the preview.
@currentFolder = new FilesCollection()
@file1 = new File({
cid: '1'
name:'Test File.file1'
'content-type': 'unknown/unknown'
}, {preflightUrl: ''})
@file2 = new File({
cid: '2'
name:'Test File.file2'
'content-type': 'unknown/unknown'
}, {preflightUrl: ''})
@file3 = new File({
cid: '3'
name:'Test File.file3'
'content-type': 'image/png',
'url': 'test/test/test.png'
}, {preflightUrl: ''})
@currentFolder.add(@file1)
@currentFolder.add(@file2)
@currentFolder.add(@file3)
properties =
initialItem: @file2
otherItems: @currentFolder
params: {splat: "test/test/test/"}
appElement: $('#fixtures').get(0)
# onResolvePath: ->
# currentFolder: @currentFolder
# query: {preview: '2'}
# toggleItemSelected: ->
# selectedItems: []
# areAllItemsSelected: -> false
@filePreview = React.renderComponent(FilePreview(properties), $('#fixtures')[0])
teardown: ->
Router.Link.restore()
Folder.resolvePath.restore()
React.unmountComponentAtNode($('#fixtures')[0])
test 'clicking the info button should render out the info panel', ->
infoButton = $('.ef-file-preview-header-info').get(0)
Simulate.click(infoButton)
ok $('.ef-file-preview-information').length, 'The info panel did not show'
test 'clicking the Show button should render out the footer', ->
showButton = $('.ef-file-preview-toggle').get(0)
Simulate.click(showButton)
ok $('.ef-file-preview-footer').length, 'The footer did not show'
test 'clicking the Show button should change the text to Hide', ->
showButton = $('.ef-file-preview-toggle').get(0)
Simulate.click(showButton)
ok $('.ef-file-preview-toggle').text().trim() is "Hide", 'The button text did not become Hide'
#####
## The next tests should be fixed once Simulate.keyDown is working properly.
#####
# test 'pressing the left arrow should navigate to the previous item', ->
# modal = $('.ReactModal__Overlay').get(0)
# Simulate.keyDown(modal, {keyCode: 37})
# expected = @file1.get 'name'
# actual = $('.ef-file-preview-header-filename').text()
# ok actual is expected, 'The previous item did not load'
# test 'pressing the left arrow should navigate to the last item if you are at the beginning', ->
# ok false
# test 'pressing the right arrow should navigate to the next item', ->
# ok false
# test 'pressing the right arrow should navigate to the first item if you are at the end.', ->
# ok false
test 'an image should be previewed if the content type matches', ->
@filePreview.setState(displayedItem: @file3)
ok $('.ef-file-preview-image').length, 'The image was not displayed'
test 'files that are not images should display a message indicating they are not able to be viewed currently', ->
# TODO: Remove this test when the rest of the preview stuff is enabled.
@filePreview.setState(displayedItem: @file1)
ok $('.ef-file-preview-not-available').length, 'The not available message was not shown.'

View File

@ -5,9 +5,16 @@ define [
'compiled/collections/FilesCollection'
], (React, Router, SearchResults, FilesCollection) ->
module 'SearchResults#render',
test 'when collection is loaded and empty display no matches found', ->
setup: ->
sinon.stub(Router, 'Link').returns('link')
sinon.stub($, 'ajax').returns($.Deferred().resolve())
teardown: ->
Router.Link.restore()
$.ajax.restore()
test 'when collection is loaded and empty display no matches found', ->
props =
params: {}
query: {}
@ -28,5 +35,4 @@ define [
ok @searchResults.refs.noResultsFound, 'Displays the no results text'
React.unmountComponentAtNode($('#fixtures')[0])
Router.Link.restore()
$.ajax.restore()

View File

@ -3,12 +3,14 @@ define [
'react'
'react-router'
'compiled/react_files/components/Toolbar'
], ($, React, Router, Toolbar) ->
'compiled/react_files/routes'
], ($, React, Router, Toolbar, routes) ->
Simulate = React.addons.TestUtils.Simulate
module 'Toolbar',
setup: ->
@routes = React.addons.TestUtils.renderIntoDocument(routes)
@toolbar = React.renderComponent(Toolbar({params: 'foo', query:'', selectedItems: '', contextId: "1", contextType: "courses"}), $('<div>').appendTo('body')[0])
teardown: ->
React.unmountComponentAtNode(@toolbar.getDOMNode().parentNode)