Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
8d6df183
Commit
8d6df183
authored
Jul 22, 2015
by
Davorin Sego
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Course discovery UI improvements
parent
bee837be
Hide whitespace changes
Inline
Side-by-side
Showing
44 changed files
with
1462 additions
and
1036 deletions
+1462
-1036
lms/djangoapps/branding/tests/test_page.py
+2
-2
lms/static/js/discovery/collection.js
+3
-3
lms/static/js/discovery/collections/filters.js
+18
-0
lms/static/js/discovery/discovery_factory.js
+63
-37
lms/static/js/discovery/filter_bar_view.js
+0
-119
lms/static/js/discovery/filters.js
+0
-30
lms/static/js/discovery/models/course_card.js
+0
-0
lms/static/js/discovery/models/course_discovery.js
+65
-0
lms/static/js/discovery/models/facet_option.js
+18
-0
lms/static/js/discovery/models/filter.js
+3
-5
lms/static/js/discovery/models/search_state.js
+151
-0
lms/static/js/discovery/search_facets_view.js
+0
-127
lms/static/js/discovery/views/course_card.js
+1
-1
lms/static/js/discovery/views/courses_listing.js
+8
-27
lms/static/js/discovery/views/facet.js
+0
-0
lms/static/js/discovery/views/facets.js
+0
-0
lms/static/js/discovery/views/filter_bar.js
+78
-0
lms/static/js/discovery/views/filter_label.js
+6
-10
lms/static/js/discovery/views/refine_sidebar.js
+99
-0
lms/static/js/discovery/views/search_form.js
+4
-6
lms/static/js/fixtures/discovery.html
+7
-5
lms/static/js/spec/discovery/collections/filters_spec.js
+22
-0
lms/static/js/spec/discovery/discovery_factory_spec.js
+195
-0
lms/static/js/spec/discovery/discovery_spec.js
+0
-603
lms/static/js/spec/discovery/models/course_card_spec.js
+28
-0
lms/static/js/spec/discovery/models/course_directory_spec.js
+101
-0
lms/static/js/spec/discovery/models/facet_option_spec.js
+19
-0
lms/static/js/spec/discovery/models/filter_spec.js
+16
-0
lms/static/js/spec/discovery/models/search_state_spec.js
+90
-0
lms/static/js/spec/discovery/views/course_card_spec.js
+56
-0
lms/static/js/spec/discovery/views/courses_listing_spec.js
+76
-0
lms/static/js/spec/discovery/views/filter_bar_spec.js
+53
-0
lms/static/js/spec/discovery/views/filter_label_spec.js
+40
-0
lms/static/js/spec/discovery/views/refine_sidebar_spec.js
+77
-0
lms/static/js/spec/discovery/views/search_form_spec.js
+53
-0
lms/static/js/spec/main.js
+12
-1
lms/static/sass/multicourse/_courses.scss
+64
-28
lms/templates/courseware/courses.html
+6
-3
lms/templates/discovery/course_card.underscore
+0
-0
lms/templates/discovery/facet.underscore
+16
-8
lms/templates/discovery/facet_option.underscore
+8
-6
lms/templates/discovery/filter_bar.underscore
+4
-5
lms/templates/discovery/search_facets_list.underscore
+0
-5
lms/templates/discovery/search_facets_section.underscore
+0
-5
No files found.
lms/djangoapps/branding/tests/test_page.py
View file @
8d6df183
...
...
@@ -217,7 +217,7 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase):
# assert that the course discovery UI is not present
self
.
assertNotIn
(
'Search for a course'
,
response
.
content
)
self
.
assertNotIn
(
'<aside aria-label="Refine
your s
earch" class="search-facets phone-menu">'
,
response
.
content
)
self
.
assertNotIn
(
'<aside aria-label="Refine
Your S
earch" class="search-facets phone-menu">'
,
response
.
content
)
# make sure we have the special css class on the section
self
.
assertIn
(
'<div class="courses no-course-discovery"'
,
response
.
content
)
...
...
@@ -241,7 +241,7 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase):
# assert that the course discovery UI is present
self
.
assertIn
(
'Search for a course'
,
response
.
content
)
self
.
assertIn
(
'<aside aria-label="Refine
your s
earch" class="search-facets phone-menu">'
,
response
.
content
)
self
.
assertIn
(
'<aside aria-label="Refine
Your S
earch" class="search-facets phone-menu">'
,
response
.
content
)
self
.
assertIn
(
'<div class="courses"'
,
response
.
content
)
@patch
(
'student.views.render_to_response'
,
RENDER_MOCK
)
...
...
lms/static/js/discovery/collection.js
View file @
8d6df183
...
...
@@ -2,13 +2,13 @@
define
([
'backbone'
,
'js/discovery/
result
'
],
function
(
Backbone
,
Result
)
{
'js/discovery/
models/course_card
'
],
function
(
Backbone
,
CourseCard
)
{
'use strict'
;
return
Backbone
.
Collection
.
extend
({
model
:
Result
,
model
:
CourseCard
,
pageSize
:
20
,
totalCount
:
0
,
latestModelsCount
:
0
,
...
...
lms/static/js/discovery/collections/filters.js
0 → 100644
View file @
8d6df183
;(
function
(
define
)
{
define
([
'backbone'
,
'js/discovery/models/filter'
],
function
(
Backbone
,
Filter
)
{
'use strict'
;
return
Backbone
.
Collection
.
extend
({
model
:
Filter
,
getTerms
:
function
()
{
return
this
.
reduce
(
function
(
terms
,
filter
)
{
terms
[
filter
.
id
]
=
filter
.
get
(
'query'
);
return
terms
;
},
{});
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/discovery_factory.js
View file @
8d6df183
;(
function
(
define
)
{
'use strict'
;
define
([
'backbone'
,
'js/discovery/collection'
,
'js/discovery/form'
,
'js/discovery/result_list_view'
,
'js/discovery/filter_bar_view'
,
'js/discovery/search_facets_view'
],
function
(
Backbone
,
Collection
,
Form
,
ResultListView
,
FilterBarView
,
FacetsBarView
)
{
define
([
'backbone'
,
'js/discovery/models/search_state'
,
'js/discovery/collections/filters'
,
'js/discovery/views/search_form'
,
'js/discovery/views/courses_listing'
,
'js/discovery/views/filter_bar'
,
'js/discovery/views/refine_sidebar'
],
function
(
Backbone
,
SearchState
,
Filters
,
SearchForm
,
CoursesListing
,
FilterBar
,
RefineSidebar
)
{
return
function
(
meanings
,
searchQuery
)
{
//facet types configuration - set default display names
var
facetsTypes
=
meanings
;
var
collection
=
new
Collection
([]);
var
results
=
new
ResultListView
({
collection
:
collection
});
var
dispatcher
=
_
.
clone
(
Backbone
.
Events
);
var
form
=
new
Form
();
var
filters
=
new
FilterBarView
();
var
facetsBarView
=
new
FacetsBarView
(
facetsTypes
);
var
dispatcher
=
_
.
extend
({},
Backbone
.
Events
);
var
search
=
new
SearchState
();
var
filters
=
new
Filters
();
var
listing
=
new
CoursesListing
({
model
:
search
.
discovery
});
var
form
=
new
SearchForm
();
var
filterBar
=
new
FilterBar
({
collection
:
filters
});
var
refineSidebar
=
new
RefineSidebar
({
collection
:
search
.
discovery
.
facetOptions
,
meanings
:
meanings
});
dispatcher
.
listenTo
(
form
,
'search'
,
function
(
query
)
{
filters
.
reset
();
form
.
showLoadingIndicator
();
filters
.
changeQueryFilter
(
query
);
search
.
performSearch
(
query
,
filters
.
getTerms
()
);
});
dispatcher
.
listenTo
(
filters
,
'search'
,
function
(
searchTerm
,
facets
)
{
collection
.
performSearch
(
searchTerm
,
facets
);
dispatcher
.
listenTo
(
refineSidebar
,
'selectOption'
,
function
(
type
,
query
,
name
)
{
form
.
showLoadingIndicator
();
if
(
filters
.
get
(
type
))
{
removeFilter
(
type
);
}
else
{
filters
.
add
({
type
:
type
,
query
:
query
,
name
:
name
});
search
.
refineSearch
(
filters
.
getTerms
());
}
});
dispatcher
.
listenTo
(
filterBar
,
'clearFilter'
,
removeFilter
);
dispatcher
.
listenTo
(
filterBar
,
'clearAll'
,
function
()
{
form
.
doSearch
(
''
);
});
dispatcher
.
listenTo
(
filters
,
'clear'
,
function
()
{
form
.
clearSearch
();
collection
.
performSearch
();
filters
.
hideClearAllButton
();
dispatcher
.
listenTo
(
listing
,
'next'
,
function
()
{
search
.
loadNextPage
()
});
dispatcher
.
listenTo
(
results
,
'next'
,
function
()
{
collection
.
loadNextPage
();
form
.
showLoadingIndicator
();
dispatcher
.
listenTo
(
search
,
'next'
,
function
()
{
listing
.
renderNext
();
});
dispatcher
.
listenTo
(
collection
,
'search'
,
function
()
{
if
(
collection
.
length
>
0
)
{
form
.
showFoundMessage
(
collection
.
totalCount
);
results
.
render
();
dispatcher
.
listenTo
(
search
,
'search'
,
function
(
query
,
total
)
{
if
(
total
>
0
)
{
form
.
showFoundMessage
(
total
);
if
(
query
)
{
filters
.
add
(
{
type
:
'search_query'
,
query
:
query
,
name
:
quote
(
query
)},
{
merge
:
true
}
);
}
}
else
{
form
.
showNotFoundMessage
(
collection
.
searchTerm
);
form
.
showNotFoundMessage
(
query
);
filters
.
reset
();
}
facetsBarView
.
renderFacets
(
collection
.
facets
);
form
.
hideLoadingIndicator
();
listing
.
render
();
refineSidebar
.
render
();
});
dispatcher
.
listenTo
(
collection
,
'next'
,
function
()
{
results
.
renderNext
();
form
.
hideLoadingIndicator
();
});
dispatcher
.
listenTo
(
collection
,
'error'
,
function
()
{
dispatcher
.
listenTo
(
search
,
'error'
,
function
()
{
form
.
showErrorMessage
();
form
.
hideLoadingIndicator
();
});
dispatcher
.
listenTo
(
facetsBarView
,
'addFilter'
,
function
(
data
)
{
filters
.
addFilter
(
data
);
});
// kick off search on page refresh
form
.
doSearch
(
searchQuery
);
function
removeFilter
(
type
)
{
form
.
showLoadingIndicator
();
filters
.
remove
(
type
);
if
(
type
===
'search_query'
)
{
form
.
doSearch
(
''
);
}
else
{
search
.
refineSearch
(
filters
.
getTerms
());
}
}
function
quote
(
string
)
{
return
'"'
+
string
+
'"'
;
}
};
});
...
...
lms/static/js/discovery/filter_bar_view.js
deleted
100644 → 0
View file @
bee837be
;(
function
(
define
)
{
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/discovery/filters'
,
'js/discovery/filter'
,
'js/discovery/filter_view'
],
function
(
$
,
_
,
Backbone
,
gettext
,
FiltersCollection
,
Filter
,
FilterView
)
{
'use strict'
;
return
Backbone
.
View
.
extend
({
el
:
'#filter-bar'
,
tagName
:
'div'
,
templateId
:
'#filter_bar-tpl'
,
className
:
'filters hidden'
,
events
:
{
'click #clear-all-filters'
:
'clearAll'
,
'click li .discovery-button'
:
'clearFilter'
},
initialize
:
function
()
{
this
.
collection
=
new
FiltersCollection
([]);
this
.
tpl
=
_
.
template
(
$
(
this
.
templateId
).
html
());
this
.
$el
.
html
(
this
.
tpl
());
this
.
hideClearAllButton
();
this
.
filtersList
=
this
.
$el
.
find
(
'ul'
);
},
render
:
function
()
{
return
this
;
},
changeQueryFilter
:
function
(
query
)
{
var
queryModel
=
this
.
collection
.
getQueryModel
();
if
(
typeof
queryModel
!==
'undefined'
)
{
this
.
collection
.
remove
(
queryModel
);
}
if
(
query
)
{
var
data
=
{
query
:
query
,
type
:
'search_string'
};
this
.
addFilter
(
data
);
}
else
{
this
.
startSearch
();
}
},
addFilter
:
function
(
data
)
{
var
currentfilter
=
this
.
collection
.
findWhere
(
data
);
if
(
typeof
currentfilter
===
'undefined'
)
{
var
filter
=
new
Filter
(
data
);
var
filterView
=
new
FilterView
({
model
:
filter
});
this
.
collection
.
add
(
filter
);
this
.
filtersList
.
append
(
filterView
.
render
().
el
);
this
.
trigger
(
'search'
,
this
.
getSearchTerm
(),
this
.
collection
);
if
(
this
.
$el
.
hasClass
(
'hidden'
))
{
this
.
showClearAllButton
();
}
}
},
clearFilter
:
function
(
event
)
{
event
.
preventDefault
();
var
$target
=
$
(
event
.
currentTarget
);
var
clearModel
=
this
.
collection
.
findWhere
({
query
:
$target
.
data
(
'value'
),
type
:
$target
.
data
(
'type'
)
});
this
.
collection
.
remove
(
clearModel
);
this
.
startSearch
();
},
clearFilters
:
function
()
{
this
.
collection
.
reset
([]);
this
.
filtersList
.
empty
();
},
clearAll
:
function
(
event
)
{
event
.
preventDefault
();
this
.
clearFilters
();
this
.
trigger
(
'clear'
);
},
showClearAllButton
:
function
()
{
this
.
$el
.
removeClass
(
'hidden'
);
},
hideClearAllButton
:
function
()
{
this
.
$el
.
addClass
(
'hidden'
);
},
getSearchTerm
:
function
()
{
var
queryModel
=
this
.
collection
.
getQueryModel
();
if
(
typeof
queryModel
!==
'undefined'
)
{
return
queryModel
.
get
(
'query'
);
}
return
''
;
},
startSearch
:
function
()
{
if
(
this
.
collection
.
length
===
0
)
{
this
.
trigger
(
'clear'
);
}
else
{
this
.
trigger
(
'search'
,
this
.
getSearchTerm
(),
this
.
collection
);
}
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/filters.js
deleted
100644 → 0
View file @
bee837be
;(
function
(
define
)
{
define
([
'backbone'
,
'js/discovery/filter'
],
function
(
Backbone
,
Filter
)
{
'use strict'
;
return
Backbone
.
Collection
.
extend
({
model
:
Filter
,
url
:
''
,
initialize
:
function
()
{
this
.
bind
(
'remove'
,
this
.
onModelRemoved
,
this
);
},
onModelRemoved
:
function
(
model
,
collection
,
options
)
{
model
.
cleanModelView
();
},
getQueryModel
:
function
()
{
return
this
.
findWhere
({
'type'
:
'search_string'
});
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/
result
.js
→
lms/static/js/discovery/
models/course_card
.js
View file @
8d6df183
File moved
lms/static/js/discovery/models/course_discovery.js
0 → 100644
View file @
8d6df183
;(
function
(
define
)
{
define
([
'underscore'
,
'backbone'
,
'js/discovery/models/course_card'
,
'js/discovery/models/facet_option'
,
],
function
(
_
,
Backbone
,
CourseCard
,
FacetOption
)
{
'use strict'
;
return
Backbone
.
Model
.
extend
({
url
:
'/search/course_discovery/'
,
jqhxr
:
null
,
defaults
:
{
totalCount
:
0
,
latestCount
:
0
},
initialize
:
function
()
{
this
.
courseCards
=
new
Backbone
.
Collection
([],
{
model
:
CourseCard
});
this
.
facetOptions
=
new
Backbone
.
Collection
([],
{
model
:
FacetOption
});
},
parse
:
function
(
response
)
{
var
courses
=
response
.
results
||
[];
var
facets
=
response
.
facets
||
{};
this
.
courseCards
.
add
(
_
.
pluck
(
courses
,
'data'
));
this
.
set
({
totalCount
:
response
.
total
,
latestCount
:
courses
.
length
});
var
options
=
this
.
facetOptions
;
_
(
facets
).
each
(
function
(
obj
,
key
)
{
_
(
obj
.
terms
).
each
(
function
(
count
,
term
)
{
options
.
add
({
facet
:
key
,
term
:
term
,
count
:
count
},
{
merge
:
true
});
});
});
},
reset
:
function
()
{
this
.
set
({
totalCount
:
0
,
latestCount
:
0
});
this
.
courseCards
.
reset
();
this
.
facetOptions
.
reset
();
},
latest
:
function
()
{
return
this
.
courseCards
.
last
(
this
.
get
(
'latestCount'
));
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/models/facet_option.js
0 → 100644
View file @
8d6df183
;(
function
(
define
)
{
define
([
'backbone'
],
function
(
Backbone
)
{
'use strict'
;
return
Backbone
.
Model
.
extend
({
idAttribute
:
'term'
,
defaults
:
{
facet
:
''
,
term
:
''
,
count
:
0
,
selected
:
false
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/filter.js
→
lms/static/js/discovery/
models/
filter.js
View file @
8d6df183
...
...
@@ -4,13 +4,11 @@ define(['backbone'], function (Backbone) {
'use strict'
;
return
Backbone
.
Model
.
extend
({
idAttribute
:
'type'
,
defaults
:
{
type
:
'search_query'
,
query
:
''
,
type
:
'search_string'
},
cleanModelView
:
function
()
{
this
.
destroy
();
name
:
''
}
});
...
...
lms/static/js/discovery/models/search_state.js
0 → 100644
View file @
8d6df183
;(
function
(
define
)
{
define
([
'underscore'
,
'backbone'
,
'js/discovery/models/course_discovery'
,
'js/discovery/collections/filters'
],
function
(
_
,
Backbone
,
CourseDiscovery
,
Filters
)
{
'use strict'
;
return
Backbone
.
Model
.
extend
({
page
:
0
,
pageSize
:
20
,
searchTerm
:
''
,
terms
:
{},
jqhxr
:
null
,
initialize
:
function
()
{
this
.
discovery
=
new
CourseDiscovery
();
this
.
listenTo
(
this
.
discovery
,
'sync'
,
this
.
onSync
,
this
);
this
.
listenTo
(
this
.
discovery
,
'error'
,
this
.
onError
,
this
);
},
performSearch
:
function
(
searchTerm
,
otherTerms
)
{
this
.
reset
();
this
.
searchTerm
=
searchTerm
;
if
(
otherTerms
)
{
this
.
terms
=
otherTerms
;
}
this
.
sendQuery
(
this
.
buildQuery
(
0
));
},
refineSearch
:
function
(
terms
)
{
this
.
reset
();
this
.
terms
=
terms
;
this
.
sendQuery
(
this
.
buildQuery
(
0
));
},
loadNextPage
:
function
()
{
if
(
this
.
hasNextPage
())
{
this
.
sendQuery
(
this
.
buildQuery
(
this
.
page
+
1
));
}
},
// private
hasNextPage
:
function
()
{
var
total
=
this
.
discovery
.
get
(
'totalCount'
);
return
total
-
((
this
.
page
+
1
)
*
this
.
pageSize
)
>
0
;
},
sendQuery
:
function
(
data
)
{
this
.
jqhxr
&&
this
.
jqhxr
.
abort
();
this
.
jqhxr
=
this
.
discovery
.
fetch
({
type
:
'POST'
,
data
:
data
});
return
this
.
jqhxr
;
},
buildQuery
:
function
(
pageIndex
)
{
var
data
=
{
search_string
:
this
.
searchTerm
,
page_size
:
this
.
pageSize
,
page_index
:
pageIndex
};
_
.
extend
(
data
,
this
.
terms
);
return
data
;
},
reset
:
function
()
{
this
.
discovery
.
reset
();
this
.
page
=
0
;
},
onError
:
function
(
collection
,
response
,
options
)
{
if
(
response
.
statusText
!==
'abort'
)
{
this
.
trigger
(
'error'
);
}
},
onSync
:
function
(
collection
,
response
,
options
)
{
var
total
=
this
.
discovery
.
get
(
'totalCount'
);
var
originalSearchTerm
=
this
.
searchTerm
;
if
(
options
.
data
.
page_index
===
0
)
{
if
(
total
===
0
)
{
// list all courses
this
.
cachedDiscovery
().
done
(
function
(
cached
)
{
this
.
discovery
.
courseCards
.
reset
(
cached
.
courseCards
.
toJSON
());
this
.
discovery
.
facetOptions
.
reset
(
cached
.
facetOptions
.
toJSON
());
this
.
discovery
.
set
(
'latestCount'
,
cached
.
get
(
'latestCount'
));
this
.
trigger
(
'search'
,
originalSearchTerm
,
total
);
});
this
.
searchTerm
=
''
;
this
.
terms
=
{};
}
else
{
_
.
each
(
this
.
terms
,
function
(
term
,
facet
)
{
if
(
facet
!==
'search_query'
)
{
var
option
=
this
.
discovery
.
facetOptions
.
findWhere
({
facet
:
facet
,
term
:
term
})
if
(
option
)
{
option
.
set
(
'selected'
,
true
);
}
}
},
this
);
this
.
trigger
(
'search'
,
this
.
searchTerm
,
total
);
}
}
else
{
this
.
page
=
options
.
data
.
page_index
;
this
.
trigger
(
'next'
);
}
},
// lazy load
cachedDiscovery
:
function
()
{
var
deferred
=
$
.
Deferred
();
var
self
=
this
;
if
(
this
.
cached
)
{
deferred
.
resolveWith
(
this
,
[
this
.
cached
]);
}
else
{
this
.
cached
=
new
CourseDiscovery
();
this
.
cached
.
fetch
({
type
:
'POST'
,
data
:
{
search_string
:
''
,
page_size
:
this
.
pageSize
,
page_index
:
0
},
success
:
function
(
model
,
response
,
options
)
{
deferred
.
resolveWith
(
self
,
[
model
]);
}
});
}
return
deferred
.
promise
();
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/search_facets_view.js
deleted
100644 → 0
View file @
bee837be
;(
function
(
define
)
{
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/discovery/facets_view'
,
'js/discovery/facet_view'
],
function
(
$
,
_
,
Backbone
,
gettext
,
FacetsView
,
FacetView
)
{
'use strict'
;
return
Backbone
.
View
.
extend
({
el
:
'.search-facets'
,
tagName
:
'div'
,
templateId
:
'#search_facets_list-tpl'
,
className
:
'facets'
,
facetsTypes
:
{},
moreLessLinksTpl
:
'#more_less_links-tpl'
,
events
:
{
'click li'
:
'addFacet'
,
'click .show-less'
:
'collapse'
,
'click .show-more'
:
'expand'
,
},
initialize
:
function
(
facetsTypes
)
{
if
(
facetsTypes
)
{
this
.
facetsTypes
=
facetsTypes
;
}
this
.
tpl
=
_
.
template
(
$
(
this
.
templateId
).
html
());
this
.
moreLessTpl
=
_
.
template
(
$
(
this
.
moreLessLinksTpl
).
html
());
this
.
$el
.
html
(
this
.
tpl
());
this
.
facetViews
=
[];
this
.
$facetViewsEl
=
this
.
$el
.
find
(
'.search-facets-lists'
);
},
render
:
function
()
{
return
this
;
},
collapse
:
function
(
event
)
{
var
$el
=
$
(
event
.
currentTarget
),
$more
=
$el
.
siblings
(
'.show-more'
),
$ul
=
$el
.
parent
(
'div'
).
siblings
(
'ul'
);
event
.
preventDefault
();
$ul
.
addClass
(
'collapse'
);
$el
.
addClass
(
'hidden'
);
$more
.
removeClass
(
'hidden'
);
},
expand
:
function
(
event
)
{
var
$el
=
$
(
event
.
currentTarget
),
$ul
=
$el
.
parent
(
'div'
).
siblings
(
'ul'
),
facets
=
$ul
.
find
(
'li'
).
length
,
itemHeight
=
34
;
event
.
preventDefault
();
$el
.
addClass
(
'hidden'
);
$ul
.
removeClass
(
'collapse'
);
$el
.
siblings
(
'.show-less'
).
removeClass
(
'hidden'
);
},
addFacet
:
function
(
event
)
{
event
.
preventDefault
();
var
$target
=
$
(
event
.
currentTarget
);
var
value
=
$target
.
find
(
'.facet-option'
).
data
(
'value'
);
var
name
=
$target
.
find
(
'.facet-option'
).
data
(
'text'
);
var
data
=
{
type
:
$target
.
data
(
'facet'
),
query
:
value
,
name
:
name
};
this
.
trigger
(
'addFilter'
,
data
);
},
displayName
:
function
(
name
,
term
){
if
(
this
.
facetsTypes
.
hasOwnProperty
(
name
))
{
if
(
term
)
{
if
(
typeof
this
.
facetsTypes
[
name
].
terms
!==
'undefined'
)
{
return
this
.
facetsTypes
[
name
].
terms
.
hasOwnProperty
(
term
)
?
this
.
facetsTypes
[
name
].
terms
[
term
]
:
term
;
}
else
{
return
term
;
}
}
else
if
(
this
.
facetsTypes
[
name
].
hasOwnProperty
(
'name'
))
{
return
this
.
facetsTypes
[
name
][
'name'
];
}
else
{
return
name
;
}
}
else
{
return
term
?
term
:
name
;
}
},
renderFacets
:
function
(
facets
)
{
var
self
=
this
;
// Remove old facets
$
.
each
(
this
.
facetViews
,
function
(
key
,
facetsList
)
{
facetsList
.
remove
();
});
self
.
facetViews
=
[];
// Render new facets
$
.
each
(
facets
,
function
(
name
,
stats
)
{
var
facetsView
=
new
FacetsView
();
self
.
facetViews
.
push
(
facetsView
);
self
.
$facetViewsEl
.
append
(
facetsView
.
render
(
name
,
self
.
displayName
(
name
),
stats
).
el
);
$
.
each
(
stats
.
terms
,
function
(
term
,
count
)
{
var
facetView
=
new
FacetView
();
facetsView
.
$views
.
append
(
facetView
.
render
(
name
,
self
.
displayName
(
name
,
term
),
term
,
count
).
el
);
facetsView
.
list
.
push
(
facetView
);
});
if
(
_
.
size
(
stats
.
terms
)
>
9
)
{
facetsView
.
$el
.
append
(
self
.
moreLessTpl
());
}
});
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/
result_item_view
.js
→
lms/static/js/discovery/
views/course_card
.js
View file @
8d6df183
...
...
@@ -28,7 +28,7 @@ define([
return
Backbone
.
View
.
extend
({
tagName
:
'li'
,
templateId
:
'#
result_item
-tpl'
,
templateId
:
'#
course_card
-tpl'
,
className
:
'courses-listing-item'
,
initialize
:
function
()
{
...
...
lms/static/js/discovery/
result_list_view
.js
→
lms/static/js/discovery/
views/courses_listing
.js
View file @
8d6df183
...
...
@@ -5,8 +5,8 @@ define([
'underscore'
,
'backbone'
,
'gettext'
,
'js/discovery/
result_item_view
'
],
function
(
$
,
_
,
Backbone
,
gettext
,
ResultItem
View
)
{
'js/discovery/
views/course_card
'
],
function
(
$
,
_
,
Backbone
,
gettext
,
CourseCard
View
)
{
'use strict'
;
return
Backbone
.
View
.
extend
({
...
...
@@ -32,48 +32,29 @@ define([
},
renderItems
:
function
()
{
var
latest
=
this
.
collection
.
latestModels
();
var
latest
=
this
.
model
.
latest
();
var
items
=
latest
.
map
(
function
(
result
)
{
var
item
=
new
ResultItem
View
({
model
:
result
});
var
item
=
new
CourseCard
View
({
model
:
result
});
return
item
.
render
().
el
;
},
this
);
this
.
$list
.
append
(
items
);
},
attachScrollHandler
:
function
()
{
this
.
nextScrollEvent
=
true
;
this
.
$window
.
on
(
'scroll'
,
this
.
scrollHandler
.
bind
(
this
));
this
.
$window
.
on
(
'scroll'
,
_
.
throttle
(
this
.
scrollHandler
.
bind
(
this
),
400
));
},
scrollHandler
:
function
()
{
if
(
this
.
nextScrollEvent
)
{
setTimeout
(
this
.
throttledScrollHandler
.
bind
(
this
),
400
);
this
.
nextScrollEvent
=
false
;
}
},
throttledScrollHandler
:
function
()
{
if
(
this
.
isNearBottom
())
{
this
.
scrolledToBottom
();
if
(
this
.
isNearBottom
()
&&
!
this
.
isLoading
)
{
this
.
trigger
(
'next'
);
this
.
isLoading
=
true
;
}
this
.
nextScrollEvent
=
true
;
},
isNearBottom
:
function
()
{
var
scrollBottom
=
this
.
$window
.
scrollTop
()
+
this
.
$window
.
height
();
var
threshold
=
this
.
$document
.
height
()
-
200
;
return
scrollBottom
>=
threshold
;
},
scrolledToBottom
:
function
()
{
if
(
this
.
thereIsMore
()
&&
!
this
.
isLoading
)
{
this
.
trigger
(
'next'
);
this
.
isLoading
=
true
;
}
},
thereIsMore
:
function
()
{
return
this
.
collection
.
hasNextPage
();
}
});
...
...
lms/static/js/discovery/
facet_view
.js
→
lms/static/js/discovery/
views/facet
.js
View file @
8d6df183
File moved
lms/static/js/discovery/
facets_view
.js
→
lms/static/js/discovery/
views/facets
.js
View file @
8d6df183
File moved
lms/static/js/discovery/views/filter_bar.js
0 → 100644
View file @
8d6df183
;(
function
(
define
)
{
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/discovery/models/filter'
,
'js/discovery/views/filter_label'
],
function
(
$
,
_
,
Backbone
,
gettext
,
Filter
,
FilterLabel
)
{
'use strict'
;
return
Backbone
.
View
.
extend
({
el
:
'#filter-bar'
,
templateId
:
'#filter_bar-tpl'
,
events
:
{
'click #clear-all-filters'
:
'clearAll'
,
'click li .discovery-button'
:
'clearFilter'
},
initialize
:
function
()
{
this
.
tpl
=
_
.
template
(
$
(
this
.
templateId
).
html
());
this
.
render
();
this
.
listenTo
(
this
.
collection
,
'remove'
,
this
.
hideIfEmpty
);
this
.
listenTo
(
this
.
collection
,
'add'
,
this
.
addFilter
);
this
.
listenTo
(
this
.
collection
,
'reset'
,
this
.
resetFilters
);
},
render
:
function
()
{
this
.
$el
.
html
(
this
.
tpl
());
this
.
$ul
=
this
.
$el
.
find
(
'ul'
);
this
.
$el
.
addClass
(
'is-animated'
);
return
this
;
},
addFilter
:
function
(
filter
)
{
var
label
=
new
FilterLabel
({
model
:
filter
});
this
.
$ul
.
append
(
label
.
render
().
el
);
this
.
show
();
},
hideIfEmpty
:
function
()
{
if
(
this
.
collection
.
isEmpty
())
{
this
.
hide
();
}
},
resetFilters
:
function
()
{
this
.
$ul
.
empty
();
this
.
hide
();
},
clearFilter
:
function
(
event
)
{
var
$target
=
$
(
event
.
currentTarget
);
var
filter
=
this
.
collection
.
get
(
$target
.
data
(
'type'
));
this
.
trigger
(
'clearFilter'
,
filter
.
id
);
},
clearAll
:
function
(
event
)
{
this
.
trigger
(
'clearAll'
);
},
show
:
function
()
{
this
.
$el
.
removeClass
(
'is-collapsed'
);
},
hide
:
function
()
{
this
.
$ul
.
empty
();
this
.
$el
.
addClass
(
'is-collapsed'
);
}
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/
filter_view
.js
→
lms/static/js/discovery/
views/filter_label
.js
View file @
8d6df183
...
...
@@ -4,7 +4,7 @@ define([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'gettext'
],
function
(
$
,
_
,
Backbone
,
gettext
)
{
'use strict'
;
...
...
@@ -15,21 +15,17 @@ define([
className
:
'active-filter'
,
initialize
:
function
()
{
this
.
tpl
=
_
.
template
(
$
(
this
.
templateId
).
html
());
this
.
listenTo
(
this
.
model
,
'destroy'
,
this
.
remove
);
this
.
tpl
=
_
.
template
(
$
(
'#filter-tpl'
).
html
());
this
.
listenTo
(
this
.
model
,
'remove'
,
this
.
remove
);
this
.
listenTo
(
this
.
model
,
'change'
,
this
.
render
);
},
render
:
function
()
{
this
.
className
=
this
.
model
.
get
(
'type'
);
var
data
=
this
.
model
.
attributes
;
var
data
=
_
.
clone
(
this
.
model
.
attributes
);
data
.
name
=
data
.
name
||
data
.
query
;
this
.
className
=
data
.
type
;
this
.
$el
.
html
(
this
.
tpl
(
data
));
return
this
;
},
remove
:
function
()
{
this
.
stopListening
();
this
.
$el
.
remove
();
}
});
...
...
lms/static/js/discovery/views/refine_sidebar.js
0 → 100644
View file @
8d6df183
;(
function
(
define
)
{
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
],
function
(
$
,
_
,
Backbone
,
gettext
)
{
'use strict'
;
return
Backbone
.
View
.
extend
({
el
:
'.search-facets'
,
events
:
{
'click li button'
:
'selectOption'
,
'click .show-less'
:
'collapse'
,
'click .show-more'
:
'expand'
},
initialize
:
function
(
options
)
{
this
.
meanings
=
options
.
meanings
||
{}
this
.
$container
=
this
.
$el
.
find
(
'.search-facets-lists'
);
this
.
facetTpl
=
_
.
template
(
$
(
'#facet-tpl'
).
html
());
this
.
facetOptionTpl
=
_
.
template
(
$
(
'#facet_option-tpl'
).
html
());
},
facetName
:
function
(
key
)
{
return
this
.
meanings
[
key
]
&&
this
.
meanings
[
key
].
name
||
key
;
},
termName
:
function
(
facetKey
,
termKey
)
{
return
this
.
meanings
[
facetKey
]
&&
this
.
meanings
[
facetKey
].
terms
&&
this
.
meanings
[
facetKey
].
terms
[
termKey
]
||
termKey
;
},
renderOptions
:
function
(
options
)
{
var
html
=
_
.
map
(
options
,
function
(
option
)
{
var
data
=
_
.
clone
(
option
.
attributes
);
data
.
name
=
this
.
termName
(
data
.
facet
,
data
.
term
);
return
this
.
facetOptionTpl
(
data
);
},
this
).
join
(
''
);
return
html
;
},
renderFacet
:
function
(
facetKey
,
options
)
{
return
this
.
facetTpl
({
name
:
facetKey
,
displayName
:
this
.
facetName
(
facetKey
),
options
:
this
.
renderOptions
(
options
),
listIsHuge
:
(
options
.
length
>
9
)
});
},
render
:
function
()
{
var
grouped
=
this
.
collection
.
groupBy
(
'facet'
);
var
html
=
_
.
map
(
grouped
,
function
(
options
,
facetKey
)
{
if
(
options
.
length
>
0
)
{
return
this
.
renderFacet
(
facetKey
,
options
);
}
},
this
).
join
(
''
);
this
.
$container
.
html
(
html
);
return
this
;
},
collapse
:
function
(
event
)
{
var
$el
=
$
(
event
.
currentTarget
),
$more
=
$el
.
siblings
(
'.show-more'
),
$ul
=
$el
.
parent
().
siblings
(
'ul'
);
$ul
.
addClass
(
'collapse'
);
$el
.
addClass
(
'hidden'
);
$more
.
removeClass
(
'hidden'
);
},
expand
:
function
(
event
)
{
var
$el
=
$
(
event
.
currentTarget
),
$ul
=
$el
.
parent
(
'div'
).
siblings
(
'ul'
);
$el
.
addClass
(
'hidden'
);
$ul
.
removeClass
(
'collapse'
);
$el
.
siblings
(
'.show-less'
).
removeClass
(
'hidden'
);
},
selectOption
:
function
(
event
)
{
var
$target
=
$
(
event
.
currentTarget
);
this
.
trigger
(
'selectOption'
,
$target
.
data
(
'facet'
),
$target
.
data
(
'value'
),
$target
.
data
(
'text'
)
);
},
});
});
})(
define
||
RequireJS
.
define
);
lms/static/js/discovery/form.js
→
lms/static/js/discovery/
views/search_
form.js
View file @
8d6df183
;(
function
(
define
)
{
define
([
'jquery'
,
'backbone'
],
function
(
$
,
Backbone
)
{
define
([
'jquery'
,
'backbone'
,
'gettext'
],
function
(
$
,
Backbone
,
gettext
)
{
'use strict'
;
return
Backbone
.
View
.
extend
({
el
:
'#discovery-form'
,
events
:
{
'submit form'
:
'submitForm'
,
'submit form'
:
'submitForm'
},
initialize
:
function
()
{
...
...
@@ -23,23 +23,20 @@ define(['jquery', 'backbone'], function ($, Backbone) {
},
doSearch
:
function
(
term
)
{
if
(
term
)
{
if
(
term
!==
undefined
)
{
this
.
$searchField
.
val
(
term
);
}
else
{
term
=
this
.
$searchField
.
val
();
}
this
.
trigger
(
'search'
,
$
.
trim
(
term
));
this
.
$message
.
empty
();
},
clearSearch
:
function
()
{
this
.
$message
.
empty
();
this
.
$searchField
.
val
(
''
);
},
showLoadingIndicator
:
function
()
{
this
.
$message
.
empty
();
this
.
$loadingIndicator
.
removeClass
(
'hidden'
);
},
...
...
@@ -62,6 +59,7 @@ define(['jquery', 'backbone'], function ($, Backbone) {
[
_
.
escape
(
term
)]
);
this
.
$message
.
html
(
msg
);
this
.
clearSearch
();
},
showErrorMessage
:
function
()
{
...
...
lms/static/js/fixtures/discovery.html
View file @
8d6df183
...
...
@@ -2,26 +2,28 @@
<div
id=
"discovery-form"
class=
"wrapper-search-context"
>
<div
id=
"discovery-message"
class=
"search-status-label"
></div>
<form
class=
"wrapper-search-input"
>
<input
class=
"discovery-input"
placeholder=
"Search for a course"
type=
"text"
/>
<!-- removes spacing
-->
<button
type=
"submit"
class=
"button postfix discovery-submit"
aria-label=
"Search"
>
<input
class=
"discovery-input"
placeholder=
"Search for a course"
type=
"text"
/>
<button
type=
"submit"
class=
"button postfix discovery-submit"
aria-label=
"Search"
>
<i
class=
"icon fa fa-search"
aria-hidden=
"true"
></i>
<div
aria-live=
"polite"
aria-relevant=
"all"
>
<div
id=
"loading-indicator"
class=
"loading-spinner hidden"
>
<i
class=
"icon fa fa-spinner fa-spin"
aria-hidden=
"true"
></i>
<i
class=
"icon fa fa-spinner fa-spin"
></i>
<span
class=
"sr"
>
Loading
</span>
</div>
</div>
</button>
</form>
<div
id=
"filter-bar"
class=
"filters hide-phone"
>
</div>
<div
class=
"courses"
role=
"region"
aria-label=
"
${_('List of Courses')}
"
>
<div
class=
"courses"
role=
"region"
aria-label=
"
List of Courses
"
>
<ul
class=
"courses-listing"
></ul>
</div>
<aside
aria-label=
"Refine your search"
class=
"search-facets phone-menu"
>
<h2
class=
"header-search-facets"
>
Refine Your Search
</h2>
<section
class=
"search-facets-lists"
></section>
</aside>
</section>
...
...
lms/static/js/spec/discovery/collections/filters_spec.js
0 → 100644
View file @
8d6df183
define
([
'js/discovery/collections/filters'
],
function
(
Filters
)
{
'use strict'
;
describe
(
'discovery.collections.Filters'
,
function
()
{
beforeEach
(
function
()
{
this
.
filters
=
new
Filters
([
{
type
:
'org'
,
query
:
'edX'
,
name
:
'edX'
},
{
type
:
'language'
,
query
:
'en'
,
name
:
'English'
}
]);
});
it
(
'converts to a dictionary'
,
function
()
{
expect
(
this
.
filters
.
getTerms
()).
toEqual
({
org
:
'edX'
,
language
:
'en'
});
});
});
});
lms/static/js/spec/discovery/discovery_factory_spec.js
0 → 100644
View file @
8d6df183
define
([
'jquery'
,
'common/js/spec_helpers/ajax_helpers'
,
'common/js/spec_helpers/template_helpers'
,
'js/discovery/discovery_factory'
],
function
(
$
,
AjaxHelpers
,
TemplateHelpers
,
DiscoveryFactory
)
{
'use strict'
;
var
MEANINGS
=
{
org
:
{
name
:
'Organization'
,
terms
:
{
edX1
:
"edX_1"
}
},
modes
:
{
name
:
'Course Type'
,
terms
:
{
honor
:
'Honor'
,
verified
:
'Verified'
}
},
language
:
{
terms
:
{
en
:
'English'
,
hr
:
'Croatian'
}
}
};
var
JSON_RESPONSE
=
{
"total"
:
365
,
"results"
:
[
{
"data"
:
{
"modes"
:
[
"honor"
],
"course"
:
"edX/DemoX/Demo_Course"
,
"enrollment_start"
:
"2015-04-21T00:00:00+00:00"
,
"number"
:
"DemoX"
,
"content"
:
{
"overview"
:
" About This Course Include your long course description here."
,
"display_name"
:
"edX Demonstration Course"
,
"number"
:
"DemoX"
},
"start"
:
"1970-01-01T05:00:00+00:00"
,
"image_url"
:
"/c4x/edX/DemoX/asset/images_course_image.jpg"
,
"org"
:
"edX"
,
"id"
:
"edX/DemoX/Demo_Course"
}
}
],
"facets"
:
{
"org"
:
{
"total"
:
26
,
"terms"
:
{
"edX1"
:
1
,
"edX2"
:
1
,
"edX3"
:
1
,
"edX4"
:
1
,
"edX5"
:
1
,
"edX6"
:
1
,
"edX7"
:
1
,
"edX8"
:
1
,
"edX9"
:
1
,
"edX10"
:
1
,
"edX11"
:
1
,
"edX12"
:
1
,
"edX13"
:
1
,
"edX14"
:
1
,
"edX15"
:
1
,
"edX16"
:
1
,
"edX17"
:
1
,
"edX18"
:
1
,
"edX19"
:
1
,
"edX20"
:
1
,
"edX21"
:
1
,
"edX22"
:
1
,
"edX23"
:
1
,
"edX24"
:
1
,
"edX25"
:
1
,
"edX26"
:
1
},
"other"
:
0
},
"modes"
:
{
"total"
:
1
,
"terms"
:
{
"honor"
:
1
},
"other"
:
0
}
}
};
describe
(
'discovery.DiscoveryFactory'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplates
([
'templates/discovery/course_card'
,
'templates/discovery/facet'
,
'templates/discovery/facet_option'
,
'templates/discovery/filter'
,
'templates/discovery/filter_bar'
]);
DiscoveryFactory
(
MEANINGS
);
});
it
(
'does search'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
1
);
expect
(
$
(
'.courses-listing .course-title'
)).
toContainHtml
(
'edX Demonstration Course'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
});
it
(
'loads more'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
jasmine
.
Clock
.
useMock
();
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
1
);
expect
(
$
(
'.courses-listing .course-title'
)).
toContainHtml
(
'edX Demonstration Course'
);
window
.
scroll
(
0
,
$
(
document
).
height
());
$
(
window
).
trigger
(
'scroll'
);
jasmine
.
Clock
.
tick
(
500
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
2
);
});
it
(
'displays not found message'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'asdfasdf'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
{});
expect
(
$
(
'.discovery-input'
).
val
()).
toEqual
(
''
);
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
expect
(
$
(
'.courses-listing'
)).
toBeEmpty
();
});
it
(
'displays error message'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'asdfasdf'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithError
(
requests
,
404
);
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
expect
(
$
(
'.courses-listing'
)).
toBeEmpty
();
});
it
(
'check filters and bar removed on clear all'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
expect
(
$
(
'#filter-bar'
)).
not
.
toHaveClass
(
'is-collapsed'
);
$
(
'#clear-all-filters'
).
trigger
(
'click'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
0
);
expect
(
$
(
'#filter-bar'
)).
toHaveClass
(
'is-collapsed'
);
});
it
(
'check filters and bar removed on last filter cleared'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
var
$filter
=
$
(
'.active-filter'
);
$filter
.
find
(
'.discovery-button'
).
trigger
(
'click'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
0
);
});
it
(
'filter results by named facet'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
$
(
'.search-facets li [data-value="edX1"]'
).
trigger
(
'click'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
2
);
expect
(
$
(
'.active-filter [data-value="edX1"]'
).
length
).
toBe
(
1
);
$
(
'.search-facets li [data-value="edX1"]'
).
trigger
(
'click'
);
expect
(
$
(
'.active-filter [data-value="edX1"]'
).
length
).
toBe
(
0
);
});
});
});
lms/static/js/spec/discovery/discovery_spec.js
deleted
100644 → 0
View file @
bee837be
define
([
'jquery'
,
'backbone'
,
'logger'
,
'common/js/spec_helpers/ajax_helpers'
,
'common/js/spec_helpers/template_helpers'
,
'js/discovery/discovery_factory'
,
'js/discovery/collection'
,
'js/discovery/form'
,
'js/discovery/result'
,
'js/discovery/result_item_view'
,
'js/discovery/result_list_view'
,
'js/discovery/filter'
,
'js/discovery/filters'
,
'js/discovery/filter_bar_view'
,
'js/discovery/filter_view'
,
'js/discovery/search_facets_view'
],
function
(
$
,
Backbone
,
Logger
,
AjaxHelpers
,
TemplateHelpers
,
DiscoveryFactory
,
Collection
,
DiscoveryForm
,
ResultItem
,
ResultItemView
,
ResultListView
,
FilterModel
,
FiltersCollection
,
FiltersBarView
,
FilterView
,
SearchFacetView
)
{
'use strict'
;
var
JSON_RESPONSE
=
{
"total"
:
365
,
"results"
:
[
{
"data"
:
{
"modes"
:
[
"honor"
],
"course"
:
"edX/DemoX/Demo_Course"
,
"enrollment_start"
:
"2015-04-21T00:00:00+00:00"
,
"number"
:
"DemoX"
,
"content"
:
{
"overview"
:
" About This Course Include your long course description here."
,
"display_name"
:
"edX Demonstration Course"
,
"number"
:
"DemoX"
},
"start"
:
"1970-01-01T05:00:00+00:00"
,
"image_url"
:
"/c4x/edX/DemoX/asset/images_course_image.jpg"
,
"org"
:
"edX"
,
"id"
:
"edX/DemoX/Demo_Course"
}
}
],
"facets"
:
{
"org"
:
{
"total"
:
26
,
"terms"
:
{
"edX1"
:
1
,
"edX2"
:
1
,
"edX3"
:
1
,
"edX4"
:
1
,
"edX5"
:
1
,
"edX6"
:
1
,
"edX7"
:
1
,
"edX8"
:
1
,
"edX9"
:
1
,
"edX10"
:
1
,
"edX11"
:
1
,
"edX12"
:
1
,
"edX13"
:
1
,
"edX14"
:
1
,
"edX15"
:
1
,
"edX16"
:
1
,
"edX17"
:
1
,
"edX18"
:
1
,
"edX19"
:
1
,
"edX20"
:
1
,
"edX21"
:
1
,
"edX22"
:
1
,
"edX23"
:
1
,
"edX24"
:
1
,
"edX25"
:
1
,
"edX26"
:
1
},
"other"
:
0
},
"modes"
:
{
"total"
:
1
,
"terms"
:
{
"honor"
:
1
},
"other"
:
0
}
}
};
var
FACET_LIST
=
[
{
"type"
:
"example1"
,
"query"
:
"search1"
},
{
"type"
:
"example2"
,
"query"
:
"search2"
}
];
var
SEARCH_FILTER
=
{
"type"
:
"search_string"
,
"query"
:
"search3"
};
describe
(
'Course Discovery'
,
function
()
{
describe
(
'Collection'
,
function
()
{
beforeEach
(
function
()
{
this
.
collection
=
new
Collection
();
this
.
onSearch
=
jasmine
.
createSpy
(
'onSearch'
);
this
.
collection
.
on
(
'search'
,
this
.
onSearch
);
this
.
onNext
=
jasmine
.
createSpy
(
'onNext'
);
this
.
collection
.
on
(
'next'
,
this
.
onNext
);
this
.
onError
=
jasmine
.
createSpy
(
'onError'
);
this
.
collection
.
on
(
'error'
,
this
.
onError
);
});
it
(
'sends a request and parses the json result'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
this
.
collection
.
performSearch
(
'search string'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
this
.
onSearch
).
toHaveBeenCalled
();
expect
(
this
.
collection
.
totalCount
).
toEqual
(
365
);
expect
(
this
.
collection
.
latestModels
()[
0
].
attributes
).
toEqual
(
JSON_RESPONSE
.
results
[
0
].
data
);
expect
(
this
.
collection
.
page
).
toEqual
(
0
);
});
it
(
'handles errors'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
this
.
collection
.
performSearch
(
'search string'
);
AjaxHelpers
.
respondWithError
(
requests
);
expect
(
this
.
onSearch
).
not
.
toHaveBeenCalled
();
expect
(
this
.
onError
).
toHaveBeenCalled
();
this
.
collection
.
loadNextPage
();
AjaxHelpers
.
respondWithError
(
requests
);
expect
(
this
.
onSearch
).
not
.
toHaveBeenCalled
();
expect
(
this
.
onError
).
toHaveBeenCalled
();
});
it
(
'loads next page'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
var
response
=
{
total
:
35
,
results
:
[]
};
this
.
collection
.
loadNextPage
();
AjaxHelpers
.
respondWithJson
(
requests
,
response
);
expect
(
this
.
onNext
).
toHaveBeenCalled
();
expect
(
this
.
onError
).
not
.
toHaveBeenCalled
();
});
it
(
'sends correct paging parameters'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
var
response
=
{
total
:
52
,
results
:
[]
};
this
.
collection
.
performSearch
(
'search string'
);
AjaxHelpers
.
respondWithJson
(
requests
,
response
);
this
.
collection
.
loadNextPage
();
AjaxHelpers
.
respondWithJson
(
requests
,
response
);
spyOn
(
$
,
'ajax'
);
this
.
collection
.
loadNextPage
();
expect
(
$
.
ajax
.
mostRecentCall
.
args
[
0
].
url
).
toEqual
(
this
.
collection
.
url
);
expect
(
$
.
ajax
.
mostRecentCall
.
args
[
0
].
data
).
toEqual
({
search_string
:
'search string'
,
page_size
:
this
.
collection
.
pageSize
,
page_index
:
2
});
});
it
(
'has next page'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
var
response
=
{
total
:
35
,
access_denied_count
:
5
,
results
:
[]
};
this
.
collection
.
performSearch
(
'search string'
);
AjaxHelpers
.
respondWithJson
(
requests
,
response
);
expect
(
this
.
collection
.
hasNextPage
()).
toEqual
(
true
);
this
.
collection
.
loadNextPage
();
AjaxHelpers
.
respondWithJson
(
requests
,
response
);
expect
(
this
.
collection
.
hasNextPage
()).
toEqual
(
false
);
});
it
(
'resets state when performing new search'
,
function
()
{
this
.
collection
.
add
(
new
ResultItem
());
expect
(
this
.
collection
.
length
).
toEqual
(
1
);
this
.
collection
.
performSearch
(
'search string'
);
expect
(
this
.
collection
.
length
).
toEqual
(
0
);
expect
(
this
.
collection
.
page
).
toEqual
(
0
);
expect
(
this
.
collection
.
totalCount
).
toEqual
(
0
);
expect
(
this
.
collection
.
latestModelsCount
).
toEqual
(
0
);
});
});
describe
(
'ResultItem'
,
function
()
{
beforeEach
(
function
()
{
this
.
result
=
new
ResultItem
();
});
it
(
'has properties'
,
function
()
{
expect
(
this
.
result
.
get
(
'modes'
)).
toBeDefined
();
expect
(
this
.
result
.
get
(
'course'
)).
toBeDefined
();
expect
(
this
.
result
.
get
(
'enrollment_start'
)).
toBeDefined
();
expect
(
this
.
result
.
get
(
'number'
)).
toBeDefined
();
expect
(
this
.
result
.
get
(
'content'
)).
toEqual
({
display_name
:
''
,
number
:
''
,
overview
:
''
});
expect
(
this
.
result
.
get
(
'start'
)).
toBeDefined
();
expect
(
this
.
result
.
get
(
'image_url'
)).
toBeDefined
();
expect
(
this
.
result
.
get
(
'org'
)).
toBeDefined
();
expect
(
this
.
result
.
get
(
'id'
)).
toBeDefined
();
});
});
describe
(
'ResultItemView'
,
function
()
{
beforeEach
(
function
()
{
TemplateHelpers
.
installTemplate
(
'templates/discovery/result_item'
);
this
.
item
=
new
ResultItemView
({
model
:
new
ResultItem
(
JSON_RESPONSE
.
results
[
0
].
data
)
});
});
it
(
'renders correctly'
,
function
()
{
var
data
=
this
.
item
.
model
.
attributes
;
this
.
item
.
render
();
expect
(
this
.
item
.
$el
).
toContainHtml
(
data
.
content
.
display_name
);
expect
(
this
.
item
.
$el
).
toContain
(
'a[href="/courses/'
+
data
.
course
+
'/about"]'
);
expect
(
this
.
item
.
$el
).
toContain
(
'img[src="'
+
data
.
image_url
+
'"]'
);
expect
(
this
.
item
.
$el
.
find
(
'.course-name'
)).
toContainHtml
(
data
.
org
);
expect
(
this
.
item
.
$el
.
find
(
'.course-name'
)).
toContainHtml
(
data
.
content
.
number
);
expect
(
this
.
item
.
$el
.
find
(
'.course-name'
)).
toContainHtml
(
data
.
content
.
display_name
);
expect
(
this
.
item
.
$el
.
find
(
'.course-date'
)).
toContainHtml
(
'Jan 01, 1970'
);
});
});
describe
(
'DiscoveryForm'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
this
.
form
=
new
DiscoveryForm
();
this
.
onSearch
=
jasmine
.
createSpy
(
'onSearch'
);
this
.
form
.
on
(
'search'
,
this
.
onSearch
);
});
it
(
'trims input string'
,
function
()
{
var
term
=
' search string '
;
$
(
'.discovery-input'
).
val
(
term
);
$
(
'form'
).
trigger
(
'submit'
);
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
$
.
trim
(
term
));
});
it
(
'handles calls to doSearch'
,
function
()
{
var
term
=
' search string '
;
$
(
'.discovery-input'
).
val
(
term
);
this
.
form
.
doSearch
(
term
);
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
$
.
trim
(
term
));
expect
(
$
(
'.discovery-input'
).
val
()).
toEqual
(
term
);
expect
(
$
(
'#discovery-message'
)).
toBeEmpty
();
});
it
(
'clears search'
,
function
()
{
$
(
'.discovery-input'
).
val
(
'somethig'
);
this
.
form
.
clearSearch
();
expect
(
$
(
'.discovery-input'
).
val
()).
toEqual
(
''
);
});
it
(
'shows/hides loading indicator'
,
function
()
{
this
.
form
.
showLoadingIndicator
();
expect
(
$
(
'#loading-indicator'
)).
not
.
toHaveClass
(
'hidden'
);
this
.
form
.
hideLoadingIndicator
();
expect
(
$
(
'#loading-indicator'
)).
toHaveClass
(
'hidden'
);
});
it
(
'shows messages'
,
function
()
{
this
.
form
.
showNotFoundMessage
();
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
this
.
form
.
showErrorMessage
();
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
});
});
describe
(
'FilterBarView'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplates
(
[
'templates/discovery/filter_bar'
,
'templates/discovery/filter'
]
);
this
.
filterBar
=
new
FiltersBarView
();
this
.
onClear
=
jasmine
.
createSpy
(
'onClear'
);
this
.
filterBar
.
on
(
'clear'
,
this
.
onClear
);
});
it
(
'view searches for sent facet object'
,
function
()
{
expect
(
this
.
filterBar
.
$el
.
length
).
toBe
(
1
);
this
.
filterBar
.
addFilter
(
FACET_LIST
[
0
]);
expect
(
this
.
filterBar
.
$el
.
find
(
'#clear-all-filters'
)).
toBeVisible
();
});
it
(
'view searches for entered search string'
,
function
()
{
spyOn
(
this
.
filterBar
,
'addFilter'
).
andCallThrough
();
expect
(
this
.
filterBar
.
$el
.
length
).
toBe
(
1
);
this
.
filterBar
.
changeQueryFilter
(
SEARCH_FILTER
.
query
);
expect
(
this
.
filterBar
.
$el
.
find
(
'#clear-all-filters'
)).
toBeVisible
();
expect
(
this
.
filterBar
.
addFilter
).
toHaveBeenCalledWith
(
SEARCH_FILTER
);
});
it
(
'model cleans view on destruction correctly'
,
function
()
{
this
.
filterBar
.
addFilter
(
SEARCH_FILTER
);
var
model
=
this
.
filterBar
.
collection
.
findWhere
(
SEARCH_FILTER
);
expect
(
this
.
filterBar
.
$el
.
find
(
'.active-filter'
).
length
).
toBe
(
1
);
model
.
cleanModelView
();
expect
(
this
.
filterBar
.
$el
.
find
(
'.active-filter'
).
length
).
toBe
(
0
);
});
it
(
'view removes all filters and hides bar if clear all'
,
function
()
{
spyOn
(
this
.
filterBar
,
'clearAll'
).
andCallThrough
();
this
.
filterBar
.
delegateEvents
();
this
.
filterBar
.
addFilter
(
SEARCH_FILTER
);
var
clearAll
=
this
.
filterBar
.
$el
.
find
(
'#clear-all-filters'
);
expect
(
clearAll
).
toBeVisible
();
clearAll
.
trigger
(
'click'
);
expect
(
this
.
filterBar
.
clearAll
).
toHaveBeenCalled
();
expect
(
this
.
onClear
).
toHaveBeenCalled
();
});
it
(
'view hides bar if all filters removed'
,
function
()
{
spyOn
(
this
.
filterBar
,
'clearFilter'
).
andCallThrough
();
this
.
filterBar
.
delegateEvents
();
this
.
filterBar
.
addFilter
(
SEARCH_FILTER
);
var
clearAll
=
this
.
filterBar
.
$el
.
find
(
'#clear-all-filters'
);
expect
(
clearAll
).
toBeVisible
();
var
filter
=
this
.
filterBar
.
$el
.
find
(
'li .discovery-button'
);
filter
.
trigger
(
'click'
);
expect
(
this
.
filterBar
.
clearFilter
).
toHaveBeenCalled
();
expect
(
this
.
onClear
).
toHaveBeenCalled
();
});
it
(
'view changes query filter'
,
function
()
{
this
.
filterBar
.
addFilter
(
SEARCH_FILTER
);
var
filter
=
$
(
this
.
filterBar
.
$el
.
find
(
'li .discovery-button'
)[
0
]);
expect
(
filter
.
text
().
trim
()).
toBe
(
SEARCH_FILTER
.
query
);
// Have to explicitly remove model because events not dispatched
var
model
=
this
.
filterBar
.
collection
.
findWhere
(
SEARCH_FILTER
);
model
.
cleanModelView
();
this
.
filterBar
.
changeQueryFilter
(
SEARCH_FILTER
.
query
+
'2'
);
filter
=
$
(
this
.
filterBar
.
$el
.
find
(
'li .discovery-button'
)[
0
]);
expect
(
filter
.
text
().
trim
()).
toBe
(
SEARCH_FILTER
.
query
+
'2'
);
});
it
(
'view returns correct search term'
,
function
()
{
this
.
filterBar
.
addFilter
(
SEARCH_FILTER
);
expect
(
this
.
filterBar
.
getSearchTerm
()).
toBe
(
SEARCH_FILTER
.
query
);
});
});
describe
(
'SearchFacetView'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplates
([
'templates/discovery/search_facet'
,
'templates/discovery/search_facets_section'
,
'templates/discovery/search_facets_list'
,
'templates/discovery/more_less_links'
]);
var
facetsTypes
=
{
org
:
'Organization'
,
modes
:
'Course Type'
};
this
.
searchFacetView
=
new
SearchFacetView
(
facetsTypes
);
this
.
searchFacetView
.
renderFacets
(
JSON_RESPONSE
.
facets
);
this
.
onAddFilter
=
jasmine
.
createSpy
(
'onAddFilter'
);
this
.
searchFacetView
.
on
(
'addFilter'
,
this
.
onAddFilter
);
});
it
(
'view expands more content on show more click'
,
function
()
{
var
$showMore
=
this
.
searchFacetView
.
$el
.
find
(
'.show-more'
);
var
$showLess
=
this
.
searchFacetView
.
$el
.
find
(
'.show-less'
);
var
$ul
=
$showMore
.
parent
(
'div'
).
siblings
(
'ul'
);
expect
(
$showMore
).
not
.
toHaveClass
(
'hidden'
);
expect
(
$showLess
).
toHaveClass
(
'hidden'
);
expect
(
$ul
).
toHaveClass
(
'collapse'
);
$showMore
.
trigger
(
'click'
);
expect
(
$showMore
).
toHaveClass
(
'hidden'
);
expect
(
$showLess
).
not
.
toHaveClass
(
'hidden'
);
expect
(
$ul
).
not
.
toHaveClass
(
'collapse'
);
});
it
(
'view collapses content on show less click'
,
function
()
{
var
$showMore
=
this
.
searchFacetView
.
$el
.
find
(
'.show-more'
);
var
$showLess
=
this
.
searchFacetView
.
$el
.
find
(
'.show-less'
);
var
$ul
=
$showMore
.
parent
(
'div'
).
siblings
(
'ul'
);
$showMore
.
trigger
(
'click'
);
expect
(
$showMore
).
toHaveClass
(
'hidden'
);
expect
(
$showLess
).
not
.
toHaveClass
(
'hidden'
);
expect
(
$ul
).
not
.
toHaveClass
(
'collapse'
);
$showLess
.
trigger
(
'click'
);
expect
(
$showMore
).
not
.
toHaveClass
(
'hidden'
);
expect
(
$showLess
).
toHaveClass
(
'hidden'
);
expect
(
$ul
).
toHaveClass
(
'collapse'
);
});
it
(
'view triggers addFilter event if facet is clicked'
,
function
()
{
this
.
searchFacetView
.
delegateEvents
();
var
$facetLink
=
this
.
searchFacetView
.
$el
.
find
(
'li [data-value="edX1"]'
);
var
$facet
=
$facetLink
.
parent
(
'li'
);
$facet
.
trigger
(
'click'
);
expect
(
this
.
onAddFilter
).
toHaveBeenCalledWith
(
{
type
:
$facet
.
data
(
'facet'
),
query
:
$facetLink
.
data
(
'value'
),
name
:
$facetLink
.
data
(
'text'
)
}
);
});
it
(
're-render facets on second click'
,
function
()
{
// First search
this
.
searchFacetView
.
delegateEvents
();
this
.
searchFacetView
.
renderFacets
(
JSON_RESPONSE
.
facets
);
expect
(
this
.
searchFacetView
.
facetViews
.
length
).
toBe
(
2
);
// Setup spy
var
customView
=
this
.
searchFacetView
.
facetViews
[
0
];
spyOn
(
customView
,
'remove'
).
andCallThrough
();
// Second search
this
.
searchFacetView
.
renderFacets
(
JSON_RESPONSE
.
facets
);
expect
(
this
.
searchFacetView
.
facetViews
.
length
).
toBe
(
2
);
expect
(
customView
.
remove
).
toHaveBeenCalled
();
});
});
describe
(
'ResultListView'
,
function
()
{
beforeEach
(
function
()
{
jasmine
.
Clock
.
useMock
();
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplate
(
'templates/discovery/result_item'
);
var
collection
=
new
Collection
([
JSON_RESPONSE
.
results
[
0
].
data
]);
collection
.
latestModelsCount
=
1
;
this
.
view
=
new
ResultListView
({
collection
:
collection
});
});
it
(
'renders search results'
,
function
()
{
this
.
view
.
render
();
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
1
);
expect
(
$
(
'.courses-listing .course-title'
)).
toContainHtml
(
'edX Demonstration Course'
);
this
.
view
.
renderNext
();
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
2
);
});
it
(
'scrolling triggers an event for next page'
,
function
()
{
this
.
onNext
=
jasmine
.
createSpy
(
'onNext'
);
this
.
view
.
on
(
'next'
,
this
.
onNext
);
spyOn
(
this
.
view
.
collection
,
'hasNextPage'
).
andCallFake
(
function
()
{
return
true
;
});
this
.
view
.
render
();
window
.
scroll
(
0
,
$
(
document
).
height
());
$
(
window
).
trigger
(
'scroll'
);
jasmine
.
Clock
.
tick
(
500
);
expect
(
this
.
onNext
).
toHaveBeenCalled
();
// should not be triggered again (while it is loading)
$
(
window
).
trigger
(
'scroll'
);
jasmine
.
Clock
.
tick
(
500
);
expect
(
this
.
onNext
.
calls
.
length
).
toEqual
(
1
);
});
});
describe
(
'Discovery Factory'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplates
([
'templates/discovery/result_item'
,
'templates/discovery/filter'
,
'templates/discovery/filter_bar'
,
'templates/discovery/search_facet'
,
'templates/discovery/search_facets_section'
,
'templates/discovery/search_facets_list'
,
'templates/discovery/more_less_links'
]);
DiscoveryFactory
(
{
org
:
{
name
:
'Organization'
,
terms
:
{
edX1
:
"edX_1"
}
},
modes
:
{
name
:
'Course Type'
,
terms
:
{
honor
:
'Honor'
,
verified
:
'Verified'
}
},
language
:
{
en
:
'English'
,
hr
:
'Croatian'
}
}
);
});
it
(
'performs search'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
1
);
expect
(
$
(
'.courses-listing .course-title'
)).
toContainHtml
(
'edX Demonstration Course'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
});
it
(
'loads more'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
jasmine
.
Clock
.
useMock
();
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
1
);
expect
(
$
(
'.courses-listing .course-title'
)).
toContainHtml
(
'edX Demonstration Course'
);
window
.
scroll
(
0
,
$
(
document
).
height
());
$
(
window
).
trigger
(
'scroll'
);
jasmine
.
Clock
.
tick
(
500
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
2
);
});
it
(
'displays not found message'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'asdfasdf'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
{});
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
expect
(
$
(
'.courses-listing'
)).
toBeEmpty
();
});
it
(
'displays error message'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'asdfasdf'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithError
(
requests
,
404
);
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
expect
(
$
(
'.courses-listing'
)).
toBeEmpty
();
});
it
(
'check filters and bar removed on clear all'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
expect
(
$
(
'#filter-bar'
)).
not
.
toHaveClass
(
'hidden'
);
$
(
'#clear-all-filters'
).
trigger
(
'click'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
0
);
expect
(
$
(
'#filter-bar'
)).
toHaveClass
(
'hidden'
);
});
it
(
'check filters and bar removed on last filter cleared'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
var
$filter
=
$
(
'.active-filter'
);
$filter
.
find
(
'.discovery-button'
).
trigger
(
'click'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
0
);
});
it
(
'filter results by named facet'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
$
(
'.discovery-input'
).
val
(
'test'
);
$
(
'.discovery-submit'
).
trigger
(
'click'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
1
);
var
$facetLink
=
$
(
'.search-facets li [data-value="edX1"]'
);
var
$facet
=
$facetLink
.
parent
(
'li'
);
$facet
.
trigger
(
'click'
);
expect
(
$
(
'.active-filter'
).
length
).
toBe
(
2
);
expect
(
$
(
'.active-filter [data-value="edX1"]'
).
length
).
toBe
(
1
);
expect
(
$
(
'.active-filter [data-value="edX1"]'
).
text
().
trim
()).
toBe
(
"edX_1"
);
});
});
});
});
lms/static/js/spec/discovery/models/course_card_spec.js
0 → 100644
View file @
8d6df183
define
([
'js/discovery/models/course_card'
],
function
(
CourseCard
)
{
'use strict'
;
describe
(
'discovery.models.CourseCard'
,
function
()
{
beforeEach
(
function
()
{
this
.
card
=
new
CourseCard
();
});
it
(
'has properties'
,
function
()
{
expect
(
this
.
card
.
get
(
'modes'
)).
toBeDefined
();
expect
(
this
.
card
.
get
(
'course'
)).
toBeDefined
();
expect
(
this
.
card
.
get
(
'enrollment_start'
)).
toBeDefined
();
expect
(
this
.
card
.
get
(
'number'
)).
toBeDefined
();
expect
(
this
.
card
.
get
(
'content'
)).
toEqual
({
display_name
:
''
,
number
:
''
,
overview
:
''
});
expect
(
this
.
card
.
get
(
'start'
)).
toBeDefined
();
expect
(
this
.
card
.
get
(
'image_url'
)).
toBeDefined
();
expect
(
this
.
card
.
get
(
'org'
)).
toBeDefined
();
expect
(
this
.
card
.
get
(
'id'
)).
toBeDefined
();
});
});
});
lms/static/js/spec/discovery/models/course_directory_spec.js
0 → 100644
View file @
8d6df183
define
([
'common/js/spec_helpers/ajax_helpers'
,
'js/discovery/models/course_discovery'
],
function
(
AjaxHelpers
,
CourseDiscovery
)
{
'use strict'
;
var
JSON_RESPONSE
=
{
"total"
:
365
,
"results"
:
[
{
"data"
:
{
"modes"
:
[
"honor"
],
"course"
:
"edX/DemoX/Demo_Course"
,
"enrollment_start"
:
"2015-04-21T00:00:00+00:00"
,
"number"
:
"DemoX"
,
"content"
:
{
"overview"
:
" About This Course Include your long course description here."
,
"display_name"
:
"edX Demonstration Course"
,
"number"
:
"DemoX"
},
"start"
:
"1970-01-01T05:00:00+00:00"
,
"image_url"
:
"/c4x/edX/DemoX/asset/images_course_image.jpg"
,
"org"
:
"edX"
,
"id"
:
"edX/DemoX/Demo_Course"
}
}
],
"facets"
:
{
"org"
:
{
"total"
:
26
,
"terms"
:
{
"edX1"
:
1
,
"edX2"
:
1
,
"edX3"
:
1
,
"edX4"
:
1
,
"edX5"
:
1
,
"edX6"
:
1
,
"edX7"
:
1
,
"edX8"
:
1
,
"edX9"
:
1
,
"edX10"
:
1
,
"edX11"
:
1
,
"edX12"
:
1
,
"edX13"
:
1
,
"edX14"
:
1
,
"edX15"
:
1
,
"edX16"
:
1
,
"edX17"
:
1
,
"edX18"
:
1
,
"edX19"
:
1
,
"edX20"
:
1
,
"edX21"
:
1
,
"edX22"
:
1
,
"edX23"
:
1
,
"edX24"
:
1
,
"edX25"
:
1
,
"edX26"
:
1
},
"other"
:
0
},
"modes"
:
{
"total"
:
1
,
"terms"
:
{
"honor"
:
1
},
"other"
:
0
}
}
};
describe
(
'discovery.models.CourseDiscovery'
,
function
()
{
beforeEach
(
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
this
.
discovery
=
new
CourseDiscovery
();
this
.
discovery
.
fetch
();
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
});
it
(
'parses server response'
,
function
()
{
expect
(
this
.
discovery
.
courseCards
.
length
).
toBe
(
1
);
expect
(
this
.
discovery
.
facetOptions
.
length
).
toBe
(
27
);
});
it
(
'resets collections'
,
function
()
{
this
.
discovery
.
reset
();
expect
(
this
.
discovery
.
courseCards
.
length
).
toBe
(
0
);
expect
(
this
.
discovery
.
facetOptions
.
length
).
toBe
(
0
);
});
it
(
'returns latest course cards'
,
function
()
{
var
latest
=
this
.
discovery
.
latest
();
expect
(
latest
.
length
).
toBe
(
1
);
});
});
});
lms/static/js/spec/discovery/models/facet_option_spec.js
0 → 100644
View file @
8d6df183
define
([
'js/discovery/models/facet_option'
],
function
(
FacetOption
)
{
'use strict'
;
describe
(
'discovery.models.FacetOption'
,
function
()
{
beforeEach
(
function
()
{
this
.
filter
=
new
FacetOption
();
});
it
(
'has properties'
,
function
()
{
expect
(
this
.
filter
.
get
(
'facet'
)).
toBeDefined
();
expect
(
this
.
filter
.
get
(
'term'
)).
toBeDefined
();
expect
(
this
.
filter
.
get
(
'count'
)).
toBeDefined
();
expect
(
this
.
filter
.
get
(
'selected'
)).
toBeDefined
();
});
});
});
lms/static/js/spec/discovery/models/filter_spec.js
0 → 100644
View file @
8d6df183
define
([
'js/discovery/models/filter'
],
function
(
Filter
)
{
'use strict'
;
describe
(
'discovery.models.Filter'
,
function
()
{
beforeEach
(
function
()
{
this
.
filter
=
new
Filter
();
});
it
(
'has properties'
,
function
()
{
expect
(
this
.
filter
.
get
(
'type'
)).
toBeDefined
();
expect
(
this
.
filter
.
get
(
'query'
)).
toBeDefined
();
expect
(
this
.
filter
.
get
(
'name'
)).
toBeDefined
();
});
});
});
lms/static/js/spec/discovery/models/search_state_spec.js
0 → 100644
View file @
8d6df183
define
([
'common/js/spec_helpers/ajax_helpers'
,
'js/discovery/models/search_state'
],
function
(
AjaxHelpers
,
SearchState
)
{
'use strict'
;
var
JSON_RESPONSE
=
{
"total"
:
365
,
"results"
:
[
{
"data"
:
{
"modes"
:
[
"honor"
],
"course"
:
"edX/DemoX/Demo_Course"
,
"enrollment_start"
:
"2015-04-21T00:00:00+00:00"
,
"number"
:
"DemoX"
,
"content"
:
{
"overview"
:
" About This Course Include your long course description here."
,
"display_name"
:
"edX Demonstration Course"
,
"number"
:
"DemoX"
},
"start"
:
"1970-01-01T05:00:00+00:00"
,
"image_url"
:
"/c4x/edX/DemoX/asset/images_course_image.jpg"
,
"org"
:
"edX"
,
"id"
:
"edX/DemoX/Demo_Course"
}
}
]
};
describe
(
'discovery.models.SearchState'
,
function
()
{
beforeEach
(
function
()
{
this
.
search
=
new
SearchState
();
this
.
onSearch
=
jasmine
.
createSpy
(
'onSearch'
);
this
.
onNext
=
jasmine
.
createSpy
(
'onNext'
);
this
.
onError
=
jasmine
.
createSpy
(
'onError'
);
this
.
search
.
on
(
'search'
,
this
.
onSearch
);
this
.
search
.
on
(
'next'
,
this
.
onNext
);
this
.
search
.
on
(
'error'
,
this
.
onError
);
});
it
(
'perform search'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
this
.
search
.
performSearch
(
'dummy'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
'dummy'
,
365
);
expect
(
this
.
search
.
discovery
.
courseCards
.
length
).
toBe
(
1
);
this
.
search
.
refineSearch
({
modes
:
'honor'
});
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
'dummy'
,
365
);
});
it
(
'returns an error'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
this
.
search
.
performSearch
(
''
);
AjaxHelpers
.
respondWithError
(
requests
,
500
);
expect
(
this
.
onError
).
toHaveBeenCalled
();
});
it
(
'loads next page'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
this
.
search
.
performSearch
(
'dummy'
);
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
this
.
search
.
loadNextPage
();
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
this
.
onNext
).
toHaveBeenCalled
();
});
it
(
'shows all results when there are none'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
this
.
search
.
performSearch
(
'dummy'
,
{
modes
:
'SomeOption'
});
// no results
AjaxHelpers
.
respondWithJson
(
requests
,
{
total
:
0
});
expect
(
this
.
onSearch
).
not
.
toHaveBeenCalled
();
// there should be another Ajax call to fetch all courses
AjaxHelpers
.
respondWithJson
(
requests
,
JSON_RESPONSE
);
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
'dummy'
,
0
);
// new search
this
.
search
.
performSearch
(
'something'
);
// no results
AjaxHelpers
.
respondWithJson
(
requests
,
{
total
:
0
});
// should load cached results
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
'dummy'
,
0
);
});
});
});
lms/static/js/spec/discovery/views/course_card_spec.js
0 → 100644
View file @
8d6df183
define
([
'jquery'
,
'common/js/spec_helpers/template_helpers'
,
'js/discovery/models/course_card'
,
'js/discovery/views/course_card'
],
function
(
$
,
TemplateHelpers
,
CourseCard
,
CourseCardView
)
{
'use strict'
;
var
JSON_RESPONSE
=
{
"total"
:
365
,
"results"
:
[
{
"data"
:
{
"modes"
:
[
"honor"
],
"course"
:
"edX/DemoX/Demo_Course"
,
"enrollment_start"
:
"2015-04-21T00:00:00+00:00"
,
"number"
:
"DemoX"
,
"content"
:
{
"overview"
:
" About This Course Include your long course description here."
,
"display_name"
:
"edX Demonstration Course"
,
"number"
:
"DemoX"
},
"start"
:
"1970-01-01T05:00:00+00:00"
,
"image_url"
:
"/c4x/edX/DemoX/asset/images_course_image.jpg"
,
"org"
:
"edX"
,
"id"
:
"edX/DemoX/Demo_Course"
}
}
]
};
describe
(
'discovery.views.CourseCard'
,
function
()
{
beforeEach
(
function
()
{
TemplateHelpers
.
installTemplate
(
'templates/discovery/course_card'
);
this
.
view
=
new
CourseCardView
({
model
:
new
CourseCard
(
JSON_RESPONSE
.
results
[
0
].
data
)
});
this
.
view
.
render
();
});
it
(
'renders'
,
function
()
{
var
data
=
this
.
view
.
model
.
attributes
;
expect
(
this
.
view
.
$el
).
toContainHtml
(
data
.
content
.
display_name
);
expect
(
this
.
view
.
$el
).
toContain
(
'a[href="/courses/'
+
data
.
course
+
'/about"]'
);
expect
(
this
.
view
.
$el
).
toContain
(
'img[src="'
+
data
.
image_url
+
'"]'
);
expect
(
this
.
view
.
$el
.
find
(
'.course-name'
)).
toContainHtml
(
data
.
org
);
expect
(
this
.
view
.
$el
.
find
(
'.course-name'
)).
toContainHtml
(
data
.
content
.
number
);
expect
(
this
.
view
.
$el
.
find
(
'.course-name'
)).
toContainHtml
(
data
.
content
.
display_name
);
expect
(
this
.
view
.
$el
.
find
(
'.course-date'
)).
toContainHtml
(
'Jan 01, 1970'
);
});
});
});
lms/static/js/spec/discovery/views/courses_listing_spec.js
0 → 100644
View file @
8d6df183
define
([
'jquery'
,
'backbone'
,
'common/js/spec_helpers/template_helpers'
,
'js/discovery/models/course_card'
,
'js/discovery/views/courses_listing'
],
function
(
$
,
Backbone
,
TemplateHelpers
,
CourseCard
,
CoursesListing
)
{
'use strict'
;
var
JSON_RESPONSE
=
{
"total"
:
365
,
"results"
:
[
{
"data"
:
{
"modes"
:
[
"honor"
],
"course"
:
"edX/DemoX/Demo_Course"
,
"enrollment_start"
:
"2015-04-21T00:00:00+00:00"
,
"number"
:
"DemoX"
,
"content"
:
{
"overview"
:
" About This Course Include your long course description here."
,
"display_name"
:
"edX Demonstration Course"
,
"number"
:
"DemoX"
},
"start"
:
"1970-01-01T05:00:00+00:00"
,
"image_url"
:
"/c4x/edX/DemoX/asset/images_course_image.jpg"
,
"org"
:
"edX"
,
"id"
:
"edX/DemoX/Demo_Course"
}
}
]
};
describe
(
'discovery.views.CoursesListing'
,
function
()
{
beforeEach
(
function
()
{
jasmine
.
Clock
.
useMock
();
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplate
(
'templates/discovery/course_card'
);
var
collection
=
new
Backbone
.
Collection
(
[
JSON_RESPONSE
.
results
[
0
].
data
],
{
model
:
CourseCard
}
);
var
mock
=
{
collection
:
collection
,
latest
:
function
()
{
return
this
.
collection
.
last
(
20
);
}
}
this
.
view
=
new
CoursesListing
({
model
:
mock
});
});
it
(
'renders search results'
,
function
()
{
this
.
view
.
render
();
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
1
);
expect
(
$
(
'.courses-listing .course-title'
)).
toContainHtml
(
'edX Demonstration Course'
);
this
.
view
.
renderNext
();
expect
(
$
(
'.courses-listing article'
).
length
).
toEqual
(
2
);
});
it
(
'scrolling triggers an event for next page'
,
function
()
{
this
.
onNext
=
jasmine
.
createSpy
(
'onNext'
);
this
.
view
.
on
(
'next'
,
this
.
onNext
);
this
.
view
.
render
();
window
.
scroll
(
0
,
$
(
document
).
height
());
$
(
window
).
trigger
(
'scroll'
);
jasmine
.
Clock
.
tick
(
500
);
expect
(
this
.
onNext
).
toHaveBeenCalled
();
// should not be triggered again (while it is loading)
$
(
window
).
trigger
(
'scroll'
);
jasmine
.
Clock
.
tick
(
500
);
expect
(
this
.
onNext
.
calls
.
length
).
toEqual
(
1
);
});
});
});
lms/static/js/spec/discovery/views/filter_bar_spec.js
0 → 100644
View file @
8d6df183
define
([
'jquery'
,
'common/js/spec_helpers/template_helpers'
,
'js/discovery/collections/filters'
,
'js/discovery/views/filter_bar'
],
function
(
$
,
TemplateHelpers
,
Filters
,
FilterBar
)
{
'use strict'
;
describe
(
'discovery.views.FilterBar'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplates
([
'templates/discovery/filter'
,
'templates/discovery/filter_bar'
]);
this
.
filters
=
new
Filters
();
this
.
filterBar
=
new
FilterBar
({
collection
:
this
.
filters
});
this
.
filters
.
add
({
type
:
'org'
,
query
:
'edX'
,
name
:
'edX'
});
});
it
(
'adds filter'
,
function
()
{
expect
(
this
.
filterBar
.
$el
.
find
(
'button'
)).
toHaveData
(
'type'
,
'org'
);
});
it
(
'removes filter'
,
function
()
{
this
.
filters
.
remove
(
'org'
);
expect
(
this
.
filterBar
.
$el
.
find
(
'ul'
)).
toBeEmpty
();
expect
(
this
.
filterBar
.
$el
).
toHaveClass
(
'is-collapsed'
);
});
it
(
'resets filters'
,
function
()
{
this
.
filters
.
reset
();
expect
(
this
.
filterBar
.
$el
.
find
(
'ul'
)).
toBeEmpty
();
expect
(
this
.
filterBar
.
$el
).
toHaveClass
(
'is-collapsed'
);
});
it
(
'triggers events'
,
function
()
{
this
.
onClearFilter
=
jasmine
.
createSpy
(
'onClearFilter'
);
this
.
onClearAll
=
jasmine
.
createSpy
(
'onClearAll'
);
this
.
filterBar
.
on
(
'clearFilter'
,
this
.
onClearFilter
);
this
.
filterBar
.
on
(
'clearAll'
,
this
.
onClearAll
);
this
.
filterBar
.
$el
.
find
(
'button'
).
click
();
expect
(
this
.
onClearFilter
).
toHaveBeenCalledWith
(
'org'
);
this
.
filterBar
.
$el
.
find
(
'#clear-all-filters'
).
click
();
expect
(
this
.
onClearAll
).
toHaveBeenCalled
();
});
});
});
lms/static/js/spec/discovery/views/filter_label_spec.js
0 → 100644
View file @
8d6df183
define
([
'jquery'
,
'common/js/spec_helpers/template_helpers'
,
'js/discovery/models/filter'
,
'js/discovery/views/filter_label'
],
function
(
$
,
TemplateHelpers
,
Filter
,
FilterLabel
)
{
'use strict'
;
describe
(
'discovery.views.FilterLabel'
,
function
()
{
beforeEach
(
function
()
{
TemplateHelpers
.
installTemplate
(
'templates/discovery/filter'
);
var
filter
=
new
Filter
({
type
:
'language'
,
query
:
'en'
,
name
:
'English'
});
this
.
view
=
new
FilterLabel
({
model
:
filter
});
this
.
view
.
render
();
});
it
(
'renders'
,
function
()
{
var
data
=
this
.
view
.
model
.
attributes
;
expect
(
this
.
view
.
$el
.
find
(
'button'
)).
toHaveData
(
'value'
,
'en'
);
expect
(
this
.
view
.
$el
.
find
(
'button'
)).
toHaveData
(
'type'
,
'language'
);
expect
(
this
.
view
.
$el
).
toContainHtml
(
data
.
name
);
});
it
(
'renders changes'
,
function
()
{
this
.
view
.
model
.
set
(
'query'
,
'es'
);
expect
(
this
.
view
.
$el
.
find
(
'button'
)).
toHaveData
(
'value'
,
'es'
);
});
it
(
'removes itself'
,
function
()
{
// simulate removing from collection
this
.
view
.
model
.
trigger
(
'remove'
);
expect
(
this
.
view
.
$el
).
not
.
toExist
();
});
});
});
lms/static/js/spec/discovery/views/refine_sidebar_spec.js
0 → 100644
View file @
8d6df183
define
([
'jquery'
,
'common/js/spec_helpers/template_helpers'
,
'js/discovery/models/facet_option'
,
'js/discovery/views/refine_sidebar'
],
function
(
$
,
TemplateHelpers
,
FacetOption
,
RefineSidebar
)
{
'use strict'
;
var
MEANINGS
=
{
org
:
{
name
:
'Organization'
,
terms
:
{
edX1
:
"edX_1"
}
},
modes
:
{
name
:
'Course Type'
,
terms
:
{
honor
:
'Honor'
,
verified
:
'Verified'
}
},
language
:
{
terms
:
{
en
:
'English'
,
hr
:
'Croatian'
}
}
};
describe
(
'discovery.views.RefineSidebar'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
TemplateHelpers
.
installTemplates
([
'templates/discovery/facet'
,
'templates/discovery/facet_option'
]);
this
.
facetOptions
=
new
Backbone
.
Collection
([],
{
model
:
FacetOption
});
this
.
facetOptions
.
add
([
{
facet
:
'language'
,
term
:
'es'
,
count
:
12
},
{
facet
:
'language'
,
term
:
'en'
,
count
:
10
},
{
facet
:
'modes'
,
term
:
'honor'
,
count
:
2
,
selected
:
true
}
]);
this
.
sidebar
=
new
RefineSidebar
({
collection
:
this
.
facetOptions
,
meanings
:
MEANINGS
});
this
.
sidebar
.
render
();
});
it
(
'styles active filter'
,
function
()
{
expect
(
this
.
sidebar
.
$el
.
find
(
'button.selected'
)).
toHaveData
(
'facet'
,
'modes'
);
});
it
(
'styles active filter'
,
function
()
{
this
.
onSelect
=
jasmine
.
createSpy
(
'onSelect'
);
this
.
sidebar
.
on
(
'selectOption'
,
this
.
onSelect
);
this
.
sidebar
.
$el
.
find
(
'button[data-value="en"]'
).
click
();
expect
(
this
.
onSelect
).
toHaveBeenCalledWith
(
'language'
,
'en'
,
'English'
);
});
it
(
'expands and collapses facet'
,
function
()
{
var
options
=
_
.
range
(
20
).
map
(
function
(
number
)
{
return
{
facet
:
'org'
,
term
:
'test'
+
number
,
count
:
1
};
});
this
.
facetOptions
.
reset
(
options
);
this
.
sidebar
.
render
();
this
.
sidebar
.
$el
.
find
(
'.show-more'
).
click
();
expect
(
this
.
sidebar
.
$el
.
find
(
'ul.facet-list'
)).
not
.
toHaveClass
(
'collapse'
);
expect
(
this
.
sidebar
.
$el
.
find
(
'.show-more'
)).
toHaveClass
(
'hidden'
);
this
.
sidebar
.
$el
.
find
(
'.show-less'
).
click
();
expect
(
this
.
sidebar
.
$el
.
find
(
'ul.facet-list'
)).
toHaveClass
(
'collapse'
);
expect
(
this
.
sidebar
.
$el
.
find
(
'.show-less'
)).
toHaveClass
(
'hidden'
);
});
});
});
lms/static/js/spec/discovery/views/search_form_spec.js
0 → 100644
View file @
8d6df183
define
([
'jquery'
,
'js/discovery/views/search_form'
],
function
(
$
,
SearchForm
)
{
'use strict'
;
describe
(
'discovery.views.SearchForm'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/discovery.html'
);
this
.
form
=
new
SearchForm
();
this
.
onSearch
=
jasmine
.
createSpy
(
'onSearch'
);
this
.
form
.
on
(
'search'
,
this
.
onSearch
);
});
it
(
'trims input string'
,
function
()
{
var
term
=
' search string '
;
$
(
'.discovery-input'
).
val
(
term
);
$
(
'form'
).
trigger
(
'submit'
);
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
$
.
trim
(
term
));
});
it
(
'handles calls to doSearch'
,
function
()
{
var
term
=
' search string '
;
$
(
'.discovery-input'
).
val
(
term
);
this
.
form
.
doSearch
(
term
);
expect
(
this
.
onSearch
).
toHaveBeenCalledWith
(
$
.
trim
(
term
));
expect
(
$
(
'.discovery-input'
).
val
()).
toBe
(
term
);
expect
(
$
(
'#discovery-message'
)).
toBeEmpty
();
});
it
(
'clears search'
,
function
()
{
$
(
'.discovery-input'
).
val
(
'somethig'
);
this
.
form
.
clearSearch
();
expect
(
$
(
'.discovery-input'
).
val
()).
toBe
(
''
);
});
it
(
'shows/hides loading indicator'
,
function
()
{
this
.
form
.
showLoadingIndicator
();
expect
(
$
(
'#loading-indicator'
)).
not
.
toHaveClass
(
'hidden'
);
this
.
form
.
hideLoadingIndicator
();
expect
(
$
(
'#loading-indicator'
)).
toHaveClass
(
'hidden'
);
});
it
(
'shows messages'
,
function
()
{
this
.
form
.
showFoundMessage
(
123
);
expect
(
$
(
'#discovery-message'
)).
toContainHtml
(
123
);
this
.
form
.
showNotFoundMessage
();
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
this
.
form
.
showErrorMessage
();
expect
(
$
(
'#discovery-message'
)).
not
.
toBeEmpty
();
});
});
});
lms/static/js/spec/main.js
View file @
8d6df183
...
...
@@ -591,7 +591,18 @@
'lms/include/js/spec/edxnotes/plugins/caret_navigation_spec.js'
,
'lms/include/js/spec/edxnotes/collections/notes_spec.js'
,
'lms/include/js/spec/search/search_spec.js'
,
'lms/include/js/spec/discovery/discovery_spec.js'
,
'lms/include/js/spec/discovery/collections/filters_spec.js'
,
'lms/include/js/spec/discovery/models/course_card_spec.js'
,
'lms/include/js/spec/discovery/models/course_directory_spec.js'
,
'lms/include/js/spec/discovery/models/facet_option_spec.js'
,
'lms/include/js/spec/discovery/models/filter_spec.js'
,
'lms/include/js/spec/discovery/models/search_state_spec.js'
,
'lms/include/js/spec/discovery/views/course_card_spec.js'
,
'lms/include/js/spec/discovery/views/courses_listing_spec.js'
,
'lms/include/js/spec/discovery/views/filter_bar_spec.js'
,
'lms/include/js/spec/discovery/views/refine_sidebar_spec.js'
,
'lms/include/js/spec/discovery/views/search_form_spec.js'
,
'lms/include/js/spec/discovery/discovery_factory_spec.js'
,
'lms/include/js/spec/ccx/schedule_spec.js'
]);
...
...
lms/static/sass/multicourse/_courses.scss
View file @
8d6df183
...
...
@@ -274,8 +274,8 @@ $facet-background-color: #007db8;
@extend
%t-demi-strong
;
@include
border-radius
(
3px
);
@include
padding-right
(
55px
);
width
:
100%
;
border
:
2px
solid
$gray-l3
;
width
:
100%
;
height
:
$course-search-input-height
;
color
:
$black
;
font-style
:
normal
;
...
...
@@ -298,10 +298,10 @@ $facet-background-color: #007db8;
top
:
0
;
border
:
2px
solid
$m-blue-d6
;
border-radius
:
(
$baseline
*
0
.1
);
box-shadow
:
none
;
background
:
$blue
;
padding
:
0
(
$baseline
*
0
.7
);
height
:
$course-search-input-height
;
padding
:
0
(
$baseline
*
0
.7
);
background
:
$blue
;
box-shadow
:
none
;
color
:
$white
;
text-shadow
:
none
;
...
...
@@ -316,7 +316,7 @@ $facet-background-color: #007db8;
background
:
$blue
;
position
:
absolute
;
top
:
(
$baseline
*
0
.7
);
// same as padding rule for .discovery-submit
}
}
}
// +Filters and Facets - Search
...
...
@@ -326,11 +326,24 @@ $facet-background-color: #007db8;
.filters
{
@include
clearfix
();
margin-top
:
(
$baseline
/
2
);
border-top
:
2px
solid
$courseware-button-border-color
;
border-bottom
:
2px
solid
$courseware-button-border-color
;
width
:
100%
;
height
:
auto
;
max-height
:
(
$baseline
*
10
);
overflow
:
hidden
;
&
.is-animated
{
@include
transition
(
max-height
0
.3s
);
}
&
.is-collapsed
{
max-height
:
0
;
}
.filters-inner
{
@include
clearfix
();
border-top
:
2px
solid
$courseware-button-border-color
;
border-bottom
:
2px
solid
$courseware-button-border-color
;
}
ul
{
@include
padding-left
(
0
);
...
...
@@ -343,8 +356,8 @@ $facet-background-color: #007db8;
@include
margin
((
$baseline
/
2
)
,
$baseline
,
(
$baseline
/
2
)
,
0
);
position
:
relative
;
padding
:
(
$baseline
/
2
)
(
$baseline
*
0
.75
);
background
:
$courseware-button-border-color
;
width
:
auto
;
background
:
$courseware-button-border-color
;
.facet-option
{
@extend
%t-strong
;
...
...
@@ -363,8 +376,9 @@ $facet-background-color: #007db8;
@extend
%t-strong
;
margin
:
(
$baseline
/
2
);
width
:
auto
;
text-align
:
center
;
color
:
$m-blue-d5
;
text-align
:
center
;
text-transform
:
uppercase
;
}
.flt-right
{
...
...
@@ -382,8 +396,8 @@ $facet-background-color: #007db8;
box-shadow
:
1px
2px
5px
$black-t0
;
border-top
:
1px
solid
$black
;
border-bottom
:
2px
solid
$black
;
background-color
:
$white
;
max-height
:
(
$baseline
*
100
);
background-color
:
$white
;
@include
media
(
$bp-tiny
)
{
@include
fill-parent
();
...
...
@@ -426,28 +440,32 @@ $facet-background-color: #007db8;
.header-search-facets
,
.header-facet
{
@extend
%t-title6
;
@extend
%t-strong
;
padding
:
(
$baseline
/
2
);
margin
:
0
;
padding
:
(
$baseline
/
2
);
color
:
$facet-text-color
;
text-transform
:
none
;
}
.header-facet
{
margin
:
0
(
$baseline
/
2
);
padding
:
(
$baseline
/
2
)
0
;
}
.search-facets-lists
section
{
border-top
:
1px
solid
$gray-l4
;
}
.facet-list
{
@extend
%ui-no-list
;
@include
clearfix
();
padding-bottom
:
(
$baseline
/
2
);
padding-bottom
:
(
$baseline
/
2
);
&
.collapse
{
max-height
:
(
$baseline
*
14
);
max-height
:
(
$baseline
*
1
.5
*
9
)
-
(
$baseline
/
2
);
overflow
:
hidden
;
}
li
{
@include
clearfix
();
position
:
relative
;
padding
:
0
;
height
:
(
$baseline
*
1
.5
);
clear
:
both
;
}
}
...
...
@@ -457,25 +475,19 @@ $facet-background-color: #007db8;
@include
clearfix
();
@extend
%t-action3
;
@extend
%text-truncated
;
padding
:
(
$baseline
/
4
)
(
$baseline
/
2
);
border-radius
:
0px
;
opacity
:
1
;
border-radius
:
0px
;
padding
:
(
$baseline
/
4
)
(
$baseline
/
2
);
width
:
100%
;
color
:
$facet-text-color
;
li
{
@include
clearfix
();
position
:
relative
;
clear
:
both
;
padding
:
0
;
height
:
(
$baseline
*
1
.5
);
}
.count
{
@include
right
(
$baseline
*
0
.6
);
@include
text-align
(
right
);
@include
box-sizing
(
border-box
);
@include
transition
(
all
0
.2s
ease-out
);
position
:
absolute
;
width
:
(
$baseline
*
2
);
}
//STATE: hover, focus
...
...
@@ -488,6 +500,28 @@ $facet-background-color: #007db8;
color
:
$white
;
}
}
//STATE: selected (hover, focus)
&
.selected
,
&
.selected
:hover
,
&
.selected
:focus
{
background
:
$gray-l4
;
color
:
$facet-text-color
;
.count
{
color
:
$gray-l4
;
}
.count
:before
{
@include
left
(
$baseline
*
0
.75
);
position
:
absolute
;
width
:
(
$baseline
*
2
);
color
:
$gray-l1
;
font-family
:
FontAwesome
;
text-align
:
center
;
content
:
'\f00d'
;
// fa-times
}
}
}
.toggle
{
...
...
@@ -496,7 +530,9 @@ $facet-background-color: #007db8;
button
{
@extend
%t-icon6
;
@extend
%t-strong
;
padding
:
(
$baseline
/
4
)
(
$baseline
/
2
);
color
:
$facet-background-color
;
text-transform
:
uppercase
;
}
}
}
...
...
lms/templates/courseware/courses.html
View file @
8d6df183
...
...
@@ -10,7 +10,7 @@
% if settings.FEATURES.get('ENABLE_COURSE_DISCOVERY'):
<
%
block
name=
"header_extras"
>
% for template_name in ["
result_item", "filter_bar", "filter", "search_facets_list", "search_facets_section", "search_facet", "more_less_links
"]:
% for template_name in ["
course_card", "filter_bar", "filter", "facet", "facet_option
"]:
<script
type=
"text/template"
id=
"${template_name}-tpl"
>
<%
static
:
include
path
=
"discovery/${template_name}.underscore"
/>
</script>
...
...
@@ -68,7 +68,7 @@
</form>
</div>
<div
id=
"filter-bar"
class=
"filters hide-phone"
>
<div
id=
"filter-bar"
class=
"filters hide-phone
is-collapsed
"
>
</div>
% endif
...
...
@@ -84,7 +84,10 @@
% if course_discovery_enabled:
<aside
aria-label=
"${_('Refine your search')}"
class=
"search-facets phone-menu"
>
<aside
aria-label=
"${_('Refine Your Search')}"
class=
"search-facets phone-menu"
>
<h2
class=
"header-search-facets"
>
${_('Refine Your Search')}
</h2>
<section
class=
"search-facets-lists"
>
</section>
</aside>
% endif
...
...
lms/templates/discovery/
result_item
.underscore
→
lms/templates/discovery/
course_card
.underscore
View file @
8d6df183
File moved
lms/templates/discovery/
more_less_links
.underscore
→
lms/templates/discovery/
facet
.underscore
View file @
8d6df183
<div class="toggle ">
<button class="show-more discovery-button">
<%= gettext("MORE...") %>
</button>
<button class="show-less hidden discovery-button">
<%= gettext("LESS...") %>
</button>
</div>
<h3 class="header-facet">
<%= displayName %>
</h3>
<ul data-facet="<%= name %>" class="facet-list collapse">
<%= options %>
</ul>
<% if (listIsHuge) { %>
<div class="toggle ">
<button class="show-more discovery-button">
<%= gettext("More") %>
</button>
<button class="show-less hidden discovery-button">
<%= gettext("Less") %>
</button>
</div>
<% } %>
lms/templates/discovery/
search_facet
.underscore
→
lms/templates/discovery/
facet_option
.underscore
View file @
8d6df183
<button data-value="<%= term %>" data-text="<%= name %>" class="facet-option discovery-button">
<%= name %>
<span class="count">
<%= count %>
</span>
</button>
<li>
<button data-facet="<%= facet %>" data-value="<%= term %>" data-text="<%= name %>" class="facet-option discovery-button <%= selected ? 'selected' : '' %>">
<%= name %>
<span class="count">
<%= count %>
</span>
</button>
</li>
lms/templates/discovery/filter_bar.underscore
View file @
8d6df183
<ul class="active-filters facet-list">
</ul>
<span>
<button id="clear-all-filters" class="clear-filters flt-right discovery-button"><%= gettext('CLEAR ALL') %></button>
</span>
<div class="filters-inner">
<ul class="active-filters facet-list"></ul>
<button id="clear-all-filters" class="clear-filters flt-right discovery-button"><%= gettext('Clear All') %></button>
</div>
lms/templates/discovery/search_facets_list.underscore
deleted
100644 → 0
View file @
bee837be
<h2 class="header-search-facets">
<%= gettext('Refine your search') %>
</h2>
<section class="search-facets-lists">
</section>
lms/templates/discovery/search_facets_section.underscore
deleted
100644 → 0
View file @
bee837be
<h3 class="header-facet">
<%= displayName %>
</h3>
<ul data-facet="<%= name %>" class="facet-list collapse">
</ul>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment