Commit 8d6df183 by Davorin Sego

Course discovery UI improvements

parent bee837be
......@@ -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 search" class="search-facets phone-menu">', response.content)
self.assertNotIn('<aside aria-label="Refine Your Search" 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 search" class="search-facets phone-menu">', response.content)
self.assertIn('<aside aria-label="Refine Your Search" class="search-facets phone-menu">', response.content)
self.assertIn('<div class="courses"', response.content)
@patch('student.views.render_to_response', RENDER_MOCK)
......
......@@ -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,
......
;(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);
;(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+'"';
}
};
});
......
;(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);
;(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);
;(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);
;(function (define) {
define(['backbone'], function (Backbone) {
'use strict';
return Backbone.Model.extend({
idAttribute: 'term',
defaults: {
facet: '',
term: '',
count: 0,
selected: false
}
});
});
})(define || RequireJS.define);
......@@ -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: ''
}
});
......
;(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);
;(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);
......@@ -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 () {
......
......@@ -5,8 +5,8 @@ define([
'underscore',
'backbone',
'gettext',
'js/discovery/result_item_view'
], function ($, _, Backbone, gettext, ResultItemView) {
'js/discovery/views/course_card'
], function ($, _, Backbone, gettext, CourseCardView) {
'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 ResultItemView({ model: result });
var item = new CourseCardView({ 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();
}
});
......
;(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);
......@@ -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();
}
});
......
;(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);
;(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 () {
......
......@@ -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>
......
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'
});
});
});
});
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);
});
});
});
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();
});
});
});
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);
});
});
});
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();
});
});
});
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();
});
});
});
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);
});
});
});
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');
});
});
});
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);
});
});
});
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();
});
});
});
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();
});
});
});
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');
});
});
});
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();
});
});
});
......@@ -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'
]);
......
......@@ -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;
}
}
}
......
......@@ -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
......
<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>
<% } %>
<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>
<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>
<h2 class="header-search-facets">
<%= gettext('Refine your search') %>
</h2>
<section class="search-facets-lists">
</section>
<h3 class="header-facet">
<%= displayName %>
</h3>
<ul data-facet="<%= name %>" class="facet-list collapse">
</ul>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment