From 0096c80a13acf14a1b3233019394c1b154877b0f Mon Sep 17 00:00:00 2001 From: Andy Armstrong Date: Tue, 23 May 2017 16:08:51 -0400 Subject: [PATCH] Refactor course search into openedx/features --- lms/envs/common.py | 1 + lms/static/course_search | 1 + .../js/search/course/views/search_form.js | 11 -- .../search/course/views/search_item_view.js | 11 -- .../js/search/dashboard/views/search_form.js | 11 -- .../dashboard/views/search_item_view.js | 11 -- lms/static/karma_lms.conf.js | 1 + lms/static/lms/js/build.js | 4 +- lms/static/lms/js/spec/main.js | 2 +- lms/templates/courseware/courseware.html | 10 +- lms/templates/dashboard.html | 8 +- openedx/features/course_bookmarks/urls.py | 2 +- .../features/course_search/__init__.py | 0 .../fixtures}/course_search_form.html | 0 .../fixtures}/dashboard_search_form.html | 0 .../js}/collections/search_collection.js | 33 ++-- .../js}/course_search_factory.js | 12 +- .../js}/dashboard_search_factory.js | 14 +- .../course_search/js}/models/search_result.js | 6 +- .../static/course_search/js}/search_router.js | 6 +- .../course_search/js/spec}/search_spec.js | 167 ++++++------------ .../js/views/course_search_results_view.js | 23 +-- .../js/views/dashboard_search_results_view.js | 24 +-- .../course_search/js}/views/search_form.js | 20 +-- .../js}/views/search_item_view.js | 37 ++-- .../js}/views/search_results_view.js | 58 +++--- .../templates}/course_search_item.underscore | 0 .../course_search_results.underscore | 0 .../dashboard_search_item.underscore | 0 .../dashboard_search_results.underscore | 0 .../templates}/search_error.underscore | 0 .../templates}/search_loading.underscore | 0 .../features/course_search/views/__init__.py | 0 themes/edx.org/lms/templates/dashboard.html | 8 +- 34 files changed, 197 insertions(+), 284 deletions(-) create mode 120000 lms/static/course_search delete mode 100644 lms/static/js/search/course/views/search_form.js delete mode 100644 lms/static/js/search/course/views/search_item_view.js delete mode 100644 lms/static/js/search/dashboard/views/search_form.js delete mode 100644 lms/static/js/search/dashboard/views/search_item_view.js rename test_root/log/auto_screenshots/.gitkeep => openedx/features/course_search/__init__.py (100%) rename {lms/static/js/fixtures/search => openedx/features/course_search/static/course_search/fixtures}/course_search_form.html (100%) rename {lms/static/js/fixtures/search => openedx/features/course_search/static/course_search/fixtures}/dashboard_search_form.html (100%) rename {lms/static/js/search/base => openedx/features/course_search/static/course_search/js}/collections/search_collection.js (80%) rename {lms/static/js/search/course => openedx/features/course_search/static/course_search/js}/course_search_factory.js (76%) rename {lms/static/js/search/dashboard => openedx/features/course_search/static/course_search/js}/dashboard_search_factory.js (77%) rename {lms/static/js/search/base => openedx/features/course_search/static/course_search/js}/models/search_result.js (83%) rename {lms/static/js/search/base/routers => openedx/features/course_search/static/course_search/js}/search_router.js (84%) rename {lms/static/js/spec/search => openedx/features/course_search/static/course_search/js/spec}/search_spec.js (86%) rename lms/static/js/search/course/views/search_results_view.js => openedx/features/course_search/static/course_search/js/views/course_search_results_view.js (62%) rename lms/static/js/search/dashboard/views/search_results_view.js => openedx/features/course_search/static/course_search/js/views/dashboard_search_results_view.js (50%) rename {lms/static/js/search/base => openedx/features/course_search/static/course_search/js}/views/search_form.js (85%) rename {lms/static/js/search/base => openedx/features/course_search/static/course_search/js}/views/search_item_view.js (68%) rename {lms/static/js/search/base => openedx/features/course_search/static/course_search/js}/views/search_results_view.js (57%) rename {lms/templates/search => openedx/features/course_search/static/course_search/templates}/course_search_item.underscore (100%) rename {lms/templates/search => openedx/features/course_search/static/course_search/templates}/course_search_results.underscore (100%) rename {lms/templates/search => openedx/features/course_search/static/course_search/templates}/dashboard_search_item.underscore (100%) rename {lms/templates/search => openedx/features/course_search/static/course_search/templates}/dashboard_search_results.underscore (100%) rename {lms/templates/search => openedx/features/course_search/static/course_search/templates}/search_error.underscore (100%) rename {lms/templates/search => openedx/features/course_search/static/course_search/templates}/search_loading.underscore (100%) create mode 100644 openedx/features/course_search/views/__init__.py diff --git a/lms/envs/common.py b/lms/envs/common.py index 29790a0170a1..72e0b9939f3b 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2230,6 +2230,7 @@ # Features 'openedx.features.course_bookmarks', 'openedx.features.course_experience', + 'openedx.features.course_search', 'openedx.features.enterprise_support', ) diff --git a/lms/static/course_search b/lms/static/course_search new file mode 120000 index 000000000000..3cc1d2434b7e --- /dev/null +++ b/lms/static/course_search @@ -0,0 +1 @@ +../../openedx/features/course_search/static/course_search \ No newline at end of file diff --git a/lms/static/js/search/course/views/search_form.js b/lms/static/js/search/course/views/search_form.js deleted file mode 100644 index 3d2e175d2183..000000000000 --- a/lms/static/js/search/course/views/search_form.js +++ /dev/null @@ -1,11 +0,0 @@ -(function(define) { - define([ - 'js/search/base/views/search_form' - ], function(SearchForm) { - 'use strict'; - - return SearchForm.extend({ - el: '#courseware-search-bar' - }); - }); -})(define || RequireJS.define); diff --git a/lms/static/js/search/course/views/search_item_view.js b/lms/static/js/search/course/views/search_item_view.js deleted file mode 100644 index 57181fcee640..000000000000 --- a/lms/static/js/search/course/views/search_item_view.js +++ /dev/null @@ -1,11 +0,0 @@ -(function(define) { - define([ - 'js/search/base/views/search_item_view' - ], function(SearchItemView) { - 'use strict'; - - return SearchItemView.extend({ - templateId: '#course_search_item-tpl' - }); - }); -})(define || RequireJS.define); diff --git a/lms/static/js/search/dashboard/views/search_form.js b/lms/static/js/search/dashboard/views/search_form.js deleted file mode 100644 index b302235af581..000000000000 --- a/lms/static/js/search/dashboard/views/search_form.js +++ /dev/null @@ -1,11 +0,0 @@ -(function(define) { - define([ - 'js/search/base/views/search_form' - ], function(SearchForm) { - 'use strict'; - - return SearchForm.extend({ - el: '#dashboard-search-bar' - }); - }); -})(define || RequireJS.define); diff --git a/lms/static/js/search/dashboard/views/search_item_view.js b/lms/static/js/search/dashboard/views/search_item_view.js deleted file mode 100644 index 966353337749..000000000000 --- a/lms/static/js/search/dashboard/views/search_item_view.js +++ /dev/null @@ -1,11 +0,0 @@ -(function(define) { - define([ - 'js/search/base/views/search_item_view' - ], function(SearchItemView) { - 'use strict'; - - return SearchItemView.extend({ - templateId: '#dashboard_search_item-tpl' - }); - }); -})(define || RequireJS.define); diff --git a/lms/static/karma_lms.conf.js b/lms/static/karma_lms.conf.js index 62ad5df5b905..c7485af982a5 100644 --- a/lms/static/karma_lms.conf.js +++ b/lms/static/karma_lms.conf.js @@ -28,6 +28,7 @@ var options = { sourceFiles: [ {pattern: 'coffee/src/**/!(*spec).js'}, {pattern: 'course_bookmarks/**/!(*spec).js'}, + {pattern: 'course_search/**/!(*spec).js'}, {pattern: 'discussion/js/**/!(*spec).js'}, {pattern: 'js/**/!(*spec|djangojs).js'}, {pattern: 'lms/js/**/!(*spec).js'}, diff --git a/lms/static/lms/js/build.js b/lms/static/lms/js/build.js index 999b38585c40..4bacf7887380 100644 --- a/lms/static/lms/js/build.js +++ b/lms/static/lms/js/build.js @@ -19,6 +19,8 @@ */ modules: getModulesList([ 'course_bookmarks/js/course_bookmarks_factory', + 'course_search/js/course_search_factory', + 'course_search/js/dashboard_search_factory', 'discussion/js/discussion_board_factory', 'discussion/js/discussion_profile_page_factory', 'js/api_admin/catalog_preview_factory', @@ -32,8 +34,6 @@ 'js/header_factory', 'js/learner_dashboard/program_details_factory', 'js/learner_dashboard/program_list_factory', - 'js/search/course/course_search_factory', - 'js/search/dashboard/dashboard_search_factory', 'js/student_account/logistration_factory', 'js/student_account/views/account_settings_factory', 'js/student_account/views/finish_auth_factory', diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js index 28e00e726763..b23c05cc1b37 100644 --- a/lms/static/lms/js/spec/main.js +++ b/lms/static/lms/js/spec/main.js @@ -676,6 +676,7 @@ 'course_bookmarks/js/spec/bookmark_button_view_spec.js', 'course_bookmarks/js/spec/bookmarks_list_view_spec.js', 'course_bookmarks/js/spec/course_bookmarks_factory_spec.js', + 'course_search/js/spec/search_spec.js', 'discussion/js/spec/discussion_board_factory_spec.js', 'discussion/js/spec/discussion_profile_page_factory_spec.js', 'discussion/js/spec/discussion_board_view_spec.js', @@ -749,7 +750,6 @@ 'js/spec/markdown_editor_spec.js', 'js/spec/dateutil_factory_spec.js', 'js/spec/navigation_spec.js', - 'js/spec/search/search_spec.js', 'js/spec/shoppingcart/shoppingcart_spec.js', 'js/spec/staff_debug_actions_spec.js', 'js/spec/student_account/access_spec.js', diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html index 4db0b3d1fe3d..d7395b37717e 100644 --- a/lms/templates/courseware/courseware.html +++ b/lms/templates/courseware/courseware.html @@ -37,14 +37,6 @@ % endfor -% if settings.FEATURES.get('ENABLE_COURSEWARE_SEARCH'): - % for template_name in ["course_search_item", "course_search_results", "search_loading", "search_error"]: - - % endfor -% endif - % if include_special_exams is not UNDEFINED and include_special_exams: % for template_name in ["proctored-exam-status"]: % endfor - -% for template_name in ["dashboard_search_item", "dashboard_search_results", "search_loading", "search_error"]: - -% endfor <%block name="js_extra"> @@ -49,7 +43,7 @@ }); % if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'): - <%static:require_module module_name="js/search/dashboard/dashboard_search_factory" class_name="DashboardSearchFactory"> + <%static:require_module module_name="course_search/js/dashboard/dashboard_search_factory" class_name="DashboardSearchFactory"> DashboardSearchFactory(); % endif diff --git a/openedx/features/course_bookmarks/urls.py b/openedx/features/course_bookmarks/urls.py index 667e3613cb88..97c76b1b80f2 100644 --- a/openedx/features/course_bookmarks/urls.py +++ b/openedx/features/course_bookmarks/urls.py @@ -1,5 +1,5 @@ """ -Defines URLs for the course experience. +Defines URLs for course bookmarks. """ from django.conf.urls import url diff --git a/test_root/log/auto_screenshots/.gitkeep b/openedx/features/course_search/__init__.py similarity index 100% rename from test_root/log/auto_screenshots/.gitkeep rename to openedx/features/course_search/__init__.py diff --git a/lms/static/js/fixtures/search/course_search_form.html b/openedx/features/course_search/static/course_search/fixtures/course_search_form.html similarity index 100% rename from lms/static/js/fixtures/search/course_search_form.html rename to openedx/features/course_search/static/course_search/fixtures/course_search_form.html diff --git a/lms/static/js/fixtures/search/dashboard_search_form.html b/openedx/features/course_search/static/course_search/fixtures/dashboard_search_form.html similarity index 100% rename from lms/static/js/fixtures/search/dashboard_search_form.html rename to openedx/features/course_search/static/course_search/fixtures/dashboard_search_form.html diff --git a/lms/static/js/search/base/collections/search_collection.js b/openedx/features/course_search/static/course_search/js/collections/search_collection.js similarity index 80% rename from lms/static/js/search/base/collections/search_collection.js rename to openedx/features/course_search/static/course_search/js/collections/search_collection.js index 3e62446f3461..867dcb07eb8f 100644 --- a/lms/static/js/search/base/collections/search_collection.js +++ b/openedx/features/course_search/static/course_search/js/collections/search_collection.js @@ -1,10 +1,11 @@ (function(define) { + 'use strict'; + define([ + 'underscore', 'backbone', - 'js/search/base/models/search_result' - ], function(Backbone, SearchResult) { - 'use strict'; - + 'course_search/js/models/search_result' + ], function(_, Backbone, SearchResult) { return Backbone.Collection.extend({ model: SearchResult, @@ -26,7 +27,9 @@ }, performSearch: function(searchTerm) { - this.fetchXhr && this.fetchXhr.abort(); + if (this.fetchXhr) { + this.fetchXhr.abort(); + } this.searchTerm = searchTerm || ''; this.resetState(); this.fetchXhr = this.fetch({ @@ -36,17 +39,19 @@ page_index: 0 }, type: 'POST', - success: function(self, xhr) { + success: function(self) { self.trigger('search'); }, - error: function(self, xhr) { + error: function(self) { self.trigger('error'); } }); }, loadNextPage: function() { - this.fetchXhr && this.fetchXhr.abort(); + if (this.fetchXhr) { + this.fetchXhr.abort(); + } this.fetchXhr = this.fetch({ data: { search_string: this.searchTerm, @@ -54,11 +59,11 @@ page_index: this.page + 1 }, type: 'POST', - success: function(self, xhr) { - self.page += 1; + success: function(self) { + self.page += 1; // eslint-disable-line no-param-reassign self.trigger('next'); }, - error: function(self, xhr) { + error: function(self) { self.trigger('error'); }, add: true, @@ -68,7 +73,9 @@ }, cancelSearch: function() { - this.fetchXhr && this.fetchXhr.abort(); + if (this.fetchXhr) { + this.fetchXhr.abort(); + } this.resetState(); }, @@ -101,4 +108,4 @@ }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/course/course_search_factory.js b/openedx/features/course_search/static/course_search/js/course_search_factory.js similarity index 76% rename from lms/static/js/search/course/course_search_factory.js rename to openedx/features/course_search/static/course_search/js/course_search_factory.js index dce1a1acf226..e0785ed8085b 100644 --- a/lms/static/js/search/course/course_search_factory.js +++ b/openedx/features/course_search/static/course_search/js/course_search_factory.js @@ -1,14 +1,16 @@ (function(define) { 'use strict'; - define(['backbone', 'js/search/base/routers/search_router', 'js/search/course/views/search_form', - 'js/search/base/collections/search_collection', 'js/search/course/views/search_results_view'], - function(Backbone, SearchRouter, CourseSearchForm, SearchCollection, SearchResultsView) { + define([ + 'underscore', 'backbone', 'course_search/js/search_router', 'course_search/js/views/search_form', + 'course_search/js/collections/search_collection', 'course_search/js/views/course_search_results_view' + ], + function(_, Backbone, SearchRouter, CourseSearchForm, SearchCollection, CourseSearchResultsView) { return function(courseId) { var router = new SearchRouter(); var form = new CourseSearchForm(); var collection = new SearchCollection([], {courseId: courseId}); - var results = new SearchResultsView({collection: collection}); + var results = new CourseSearchResultsView({collection: collection}); var dispatcher = _.clone(Backbone.Events); dispatcher.listenTo(router, 'search', function(query) { @@ -44,4 +46,4 @@ }); }; }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/dashboard/dashboard_search_factory.js b/openedx/features/course_search/static/course_search/js/dashboard_search_factory.js similarity index 77% rename from lms/static/js/search/dashboard/dashboard_search_factory.js rename to openedx/features/course_search/static/course_search/js/dashboard_search_factory.js index 29b96d0ee43d..d11f51e64d41 100644 --- a/lms/static/js/search/dashboard/dashboard_search_factory.js +++ b/openedx/features/course_search/static/course_search/js/dashboard_search_factory.js @@ -1,12 +1,16 @@ (function(define) { 'use strict'; - define(['backbone', 'js/search/base/routers/search_router', 'js/search/dashboard/views/search_form', - 'js/search/base/collections/search_collection', 'js/search/dashboard/views/search_results_view'], - function(Backbone, SearchRouter, SearchForm, SearchCollection, SearchListView) { + define([ + 'underscore', 'backbone', 'course_search/js/search_router', 'course_search/js/views/search_form', + 'course_search/js/collections/search_collection', 'course_search/js/views/dashboard_search_results_view' + ], + function(_, Backbone, SearchRouter, SearchForm, SearchCollection, SearchListView) { return function() { var router = new SearchRouter(); - var form = new SearchForm(); + var form = new SearchForm({ + el: $('#dashboard-search-bar') + }); var collection = new SearchCollection([]); var results = new SearchListView({collection: collection}); var dispatcher = _.clone(Backbone.Events); @@ -48,4 +52,4 @@ }); }; }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/base/models/search_result.js b/openedx/features/course_search/static/course_search/js/models/search_result.js similarity index 83% rename from lms/static/js/search/base/models/search_result.js rename to openedx/features/course_search/static/course_search/js/models/search_result.js index eba650379325..825090eda907 100644 --- a/lms/static/js/search/base/models/search_result.js +++ b/openedx/features/course_search/static/course_search/js/models/search_result.js @@ -1,7 +1,7 @@ (function(define) { - define(['backbone'], function(Backbone) { - 'use strict'; + 'use strict'; + define(['backbone'], function(Backbone) { return Backbone.Model.extend({ defaults: { location: [], @@ -11,4 +11,4 @@ } }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/base/routers/search_router.js b/openedx/features/course_search/static/course_search/js/search_router.js similarity index 84% rename from lms/static/js/search/base/routers/search_router.js rename to openedx/features/course_search/static/course_search/js/search_router.js index e85bf7dea331..097995ad1ddb 100644 --- a/lms/static/js/search/base/routers/search_router.js +++ b/openedx/features/course_search/static/course_search/js/search_router.js @@ -1,7 +1,7 @@ (function(define) { - define(['backbone'], function(Backbone) { - 'use strict'; + 'use strict'; + define(['backbone'], function(Backbone) { return Backbone.Router.extend({ routes: { 'search/:query': 'search' @@ -11,4 +11,4 @@ } }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/spec/search/search_spec.js b/openedx/features/course_search/static/course_search/js/spec/search_spec.js similarity index 86% rename from lms/static/js/spec/search/search_spec.js rename to openedx/features/course_search/static/course_search/js/spec/search_spec.js index e339eb595059..fe9b09fc32f6 100644 --- a/lms/static/js/spec/search/search_spec.js +++ b/openedx/features/course_search/static/course_search/js/spec/search_spec.js @@ -5,17 +5,16 @@ define([ 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'common/js/spec_helpers/page_helpers', 'common/js/spec_helpers/template_helpers', - 'js/search/base/models/search_result', - 'js/search/base/collections/search_collection', - 'js/search/base/routers/search_router', - 'js/search/course/views/search_item_view', - 'js/search/dashboard/views/search_item_view', - 'js/search/course/views/search_form', - 'js/search/dashboard/views/search_form', - 'js/search/course/views/search_results_view', - 'js/search/dashboard/views/search_results_view', - 'js/search/course/course_search_factory', - 'js/search/dashboard/dashboard_search_factory' + 'course_search/js/models/search_result', + 'course_search/js/collections/search_collection', + 'course_search/js/search_router', + 'course_search/js/views/search_form', + 'course_search/js/views/search_item_view', + 'course_search/js/views/course_search_results_view', + 'course_search/js/views/dashboard_search_results_view', + 'course_search/js/course_search_factory', + 'course_search/js/dashboard_search_factory', + 'text!course_search/templates/course_search_item.underscore' ], function( $, Backbone, @@ -26,14 +25,13 @@ define([ SearchResult, SearchCollection, SearchRouter, - CourseSearchItemView, - DashSearchItemView, - CourseSearchForm, - DashSearchForm, + SearchForm, + SearchItemView, CourseSearchResultsView, DashSearchResultsView, CourseSearchFactory, - DashboardSearchFactory + DashboardSearchFactory, + courseSearchItemTemplate ) { 'use strict'; @@ -86,7 +84,6 @@ define([ it('sends a request and parses the json result', function() { var requests = AjaxHelpers.requests(this); - this.collection.performSearch('search string'); var response = { total: 2, access_denied_count: 1, @@ -99,6 +96,7 @@ define([ } }] }; + this.collection.performSearch('search string'); AjaxHelpers.respondWithJson(requests, response); expect(this.onSearch).toHaveBeenCalled(); @@ -221,12 +219,7 @@ define([ describe('SearchItemView', function() { - function beforeEachHelper(SearchItemView) { - TemplateHelpers.installTemplates([ - 'templates/search/course_search_item', - 'templates/search/dashboard_search_item' - ]); - + beforeEach(function() { this.model = new SearchResult({ location: ['section', 'subsection', 'unit'], content_type: 'Video', @@ -243,31 +236,37 @@ define([ url: 'path/to/content' }); - this.item = new SearchItemView({model: this.model}); + this.item = new SearchItemView({ + model: this.model, + template: courseSearchItemTemplate + }); this.item.render(); - this.seqItem = new SearchItemView({model: this.seqModel}); + this.seqItem = new SearchItemView({ + model: this.seqModel, + template: courseSearchItemTemplate + }); this.seqItem.render(); - } + }); - function rendersItem() { + it('rendersItem', function() { expect(this.item.$el).toHaveAttr('role', 'region'); expect(this.item.$el).toHaveAttr('aria-label', 'search result'); expect(this.item.$el).toContainElement('a[href="' + this.model.get('url') + '"]'); expect(this.item.$el.find('.result-type')).toContainHtml(this.model.get('content_type')); expect(this.item.$el.find('.result-excerpt')).toContainHtml(this.model.get('excerpt')); expect(this.item.$el.find('.result-location')).toContainHtml('section ▸ subsection ▸ unit'); - } + }); - function rendersSequentialItem() { + it('rendersSequentialItem', function() { expect(this.seqItem.$el).toHaveAttr('role', 'region'); expect(this.seqItem.$el).toHaveAttr('aria-label', 'search result'); expect(this.seqItem.$el).toContainElement('a[href="' + this.seqModel.get('url') + '"]'); expect(this.seqItem.$el.find('.result-type')).toBeEmpty(); expect(this.seqItem.$el.find('.result-excerpt')).toBeEmpty(); expect(this.seqItem.$el.find('.result-location')).toContainHtml('section ▸ subsection'); - } + }); - function logsSearchItemViewEvent() { + it('logsSearchItemViewEvent', function() { this.model.collection = new SearchCollection([this.model], {course_id: 'edx101'}); this.item.render(); // Mock the redirect call @@ -277,27 +276,6 @@ define([ expect(this.item.redirect).toHaveBeenCalled(); this.item.$el.trigger('click'); expect(this.item.redirect).toHaveBeenCalled(); - } - - describe('CourseSearchItemView', function() { - beforeEach(function() { - beforeEachHelper.call(this, CourseSearchItemView); - }); - it('renders items correctly', rendersItem); - it('renders Sequence items correctly', rendersSequentialItem); - it('logs view event', logsSearchItemViewEvent); - }); - - describe('DashSearchItemView', function() { - beforeEach(function() { - beforeEachHelper.call(this, DashSearchItemView); - }); - it('renders items correctly', rendersItem); - it('renders Sequence items correctly', rendersSequentialItem); - it('displays course name in breadcrumbs', function() { - expect(this.seqItem.$el.find('.result-course-name')).toContainHtml(this.model.get('course_name')); - }); - it('logs view event', logsSearchItemViewEvent); }); }); @@ -315,7 +293,7 @@ define([ $('.search-field').val(term); this.form.doSearch(term); expect(this.onSearch).toHaveBeenCalledWith($.trim(term)); - expect($('.search-field').val()).toEqual(term); + expect($('.search-field').val()).toEqual(term.trim()); expect($('.search-field')).toHaveClass('is-active'); expect($('.search-button')).toBeHidden(); expect($('.cancel-button')).toBeVisible(); @@ -350,26 +328,12 @@ define([ expect($('.search-button')).toBeVisible(); } - describe('CourseSearchForm', function() { - beforeEach(function() { - loadFixtures('js/fixtures/search/course_search_form.html'); - this.form = new CourseSearchForm(); - this.onClear = jasmine.createSpy('onClear'); - this.onSearch = jasmine.createSpy('onSearch'); - this.form.on('clear', this.onClear); - this.form.on('search', this.onSearch); - }); - it('trims input string', trimsInputString); - it('handles calls to doSearch', doesSearch); - it('triggers a search event and changes to active state', triggersSearchEvent); - it('clears search when clicking on cancel button', clearsSearchOnCancel); - it('clears search when search box is empty', clearsSearchOnEmpty); - }); - - describe('DashSearchForm', function() { + describe('SearchForm', function() { beforeEach(function() { - loadFixtures('js/fixtures/search/dashboard_search_form.html'); - this.form = new DashSearchForm(); + loadFixtures('course_search/fixtures/course_search_form.html'); + this.form = new SearchForm({ + el: '#courseware-search-bar' + }); this.onClear = jasmine.createSpy('onClear'); this.onSearch = jasmine.createSpy('onSearch'); this.form.on('clear', this.onClear); @@ -401,7 +365,9 @@ define([ function returnsToContent() { this.resultsView.clear(); - expect(this.resultsView.$contentElement).toHaveCss({'display': this.contentElementDisplayValue}); + expect(this.resultsView.$contentElement).toHaveCss({ + display: this.contentElementDisplayValue + }); expect(this.resultsView.$el).toBeHidden(); expect(this.resultsView.$el).toBeEmpty(); } @@ -467,28 +433,14 @@ define([ expect(this.resultsView.$el.find('a.search-load-next .icon')).toBeHidden(); this.resultsView.loadNext(); // toBeVisible does not work with inline - expect(this.resultsView.$el.find('a.search-load-next .icon')).toHaveCss({'display': 'inline'}); + expect(this.resultsView.$el.find('a.search-load-next .icon')).toHaveCss({ + display: 'inline' + }); this.resultsView.renderNext(); expect(this.resultsView.$el.find('a.search-load-next .icon')).toBeHidden(); } function beforeEachHelper(SearchResultsView) { - appendSetFixtures( - '
' + - '
' + - '
' + - '
' - ); - - TemplateHelpers.installTemplates([ - 'templates/search/course_search_item', - 'templates/search/dashboard_search_item', - 'templates/search/course_search_results', - 'templates/search/dashboard_search_results', - 'templates/search/search_loading', - 'templates/search/search_error' - ]); - var MockCollection = Backbone.Collection.extend({ hasNextPage: function() {}, latestModelsCount: 0, @@ -497,6 +449,14 @@ define([ return SearchCollection.prototype.latestModels.apply(this, arguments); } }); + + appendSetFixtures( + '
' + + '
' + + '
' + + '
' + ); + this.collection = new MockCollection(); this.resultsView = new SearchResultsView({collection: this.collection}); } @@ -599,13 +559,13 @@ define([ $('.cancel-button').trigger('click'); AjaxHelpers.skipResetRequest(requests); // there should be no results - expect(this.$contentElement).toHaveCss({'display': this.contentElementDisplayValue}); + expect(this.$contentElement).toHaveCss({display: this.contentElementDisplayValue}); expect(this.$searchResults).toBeHidden(); } function clearsResults() { $('.cancel-button').trigger('click'); - expect(this.$contentElement).toHaveCss({'display': this.contentElementDisplayValue}); + expect(this.$contentElement).toHaveCss({display: this.contentElementDisplayValue}); expect(this.$searchResults).toBeHidden(); } @@ -624,13 +584,14 @@ define([ } }] }; + var body; $('.search-field').val('query'); $('.search-button').trigger('click'); AjaxHelpers.respondWithJson(requests, response); expect(this.$searchResults.find('li').length).toEqual(1); expect($('.search-load-next')).toBeVisible(); $('.search-load-next').trigger('click'); - var body = requests[1].requestBody; + body = requests[1].requestBody; expect(body).toContain('search_string=query'); expect(body).toContain('page_index=1'); AjaxHelpers.respondWithJson(requests, response); @@ -644,27 +605,14 @@ define([ expect(requests[0].requestBody).toContain('search_string=query'); } - function loadTemplates() { - TemplateHelpers.installTemplates([ - 'templates/search/course_search_item', - 'templates/search/dashboard_search_item', - 'templates/search/search_loading', - 'templates/search/search_error', - 'templates/search/course_search_results', - 'templates/search/dashboard_search_results' - ]); - } - describe('CourseSearchApp', function() { beforeEach(function() { - loadFixtures('js/fixtures/search/course_search_form.html'); + var courseId = 'a/b/c'; + loadFixtures('course_search/fixtures/course_search_form.html'); appendSetFixtures( '
' + '
' ); - loadTemplates.call(this); - - var courseId = 'a/b/c'; CourseSearchFactory(courseId); spyOn(Backbone.history, 'navigate'); this.$contentElement = $('#course-content'); @@ -688,12 +636,11 @@ define([ describe('DashSearchApp', function() { beforeEach(function() { - loadFixtures('js/fixtures/search/dashboard_search_form.html'); + loadFixtures('course_search/fixtures/dashboard_search_form.html'); appendSetFixtures( '
' + '
' ); - loadTemplates.call(this); DashboardSearchFactory(); spyOn(Backbone.history, 'navigate'); diff --git a/lms/static/js/search/course/views/search_results_view.js b/openedx/features/course_search/static/course_search/js/views/course_search_results_view.js similarity index 62% rename from lms/static/js/search/course/views/search_results_view.js rename to openedx/features/course_search/static/course_search/js/views/course_search_results_view.js index 0d592c716312..57e0c37ea264 100644 --- a/lms/static/js/search/course/views/search_results_view.js +++ b/openedx/features/course_search/static/course_search/js/views/course_search_results_view.js @@ -1,22 +1,25 @@ (function(define) { - define([ - 'js/search/base/views/search_results_view', - 'js/search/course/views/search_item_view' - ], function(SearchResultsView, CourseSearchItemView) { - 'use strict'; + 'use strict'; + define([ + 'course_search/js/views/search_results_view', + 'text!course_search/templates/course_search_results.underscore', + 'text!course_search/templates/course_search_item.underscore' + ], function( + SearchResultsView, + courseSearchResultsTemplate, + courseSearchItemTemplate + ) { return SearchResultsView.extend({ el: '.courseware-results', contentElement: '#course-content', coursewareResultsWrapperElement: '.courseware-results-wrapper', - resultsTemplateId: '#course_search_results-tpl', - loadingTemplateId: '#search_loading-tpl', - errorTemplateId: '#search_error-tpl', + resultsTemplate: courseSearchResultsTemplate, + itemTemplate: courseSearchItemTemplate, events: { 'click .search-load-next': 'loadNext' }, - SearchItemView: CourseSearchItemView, clear: function() { SearchResultsView.prototype.clear.call(this); @@ -31,4 +34,4 @@ }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/dashboard/views/search_results_view.js b/openedx/features/course_search/static/course_search/js/views/dashboard_search_results_view.js similarity index 50% rename from lms/static/js/search/dashboard/views/search_results_view.js rename to openedx/features/course_search/static/course_search/js/views/dashboard_search_results_view.js index 5b8f50a65a4c..35efdb3c9dc6 100644 --- a/lms/static/js/search/dashboard/views/search_results_view.js +++ b/openedx/features/course_search/static/course_search/js/views/dashboard_search_results_view.js @@ -1,22 +1,24 @@ (function(define) { - define([ - 'js/search/base/views/search_results_view', - 'js/search/dashboard/views/search_item_view' - ], function(SearchResultsView, DashSearchItemView) { - 'use strict'; + 'use strict'; + define([ + 'course_search/js/views/search_results_view', + 'text!course_search/templates/dashboard_search_results.underscore', + 'text!course_search/templates/dashboard_search_item.underscore' + ], function( + SearchResultsView, + dashboardSearchResultsTemplate, + dashboardSearchItemTemplate + ) { return SearchResultsView.extend({ - el: '#dashboard-search-results', contentElement: '#my-courses, #profile-sidebar', - resultsTemplateId: '#dashboard_search_results-tpl', - loadingTemplateId: '#search_loading-tpl', - errorTemplateId: '#search_error-tpl', + resultsTemplate: dashboardSearchResultsTemplate, + itemTemplate: dashboardSearchItemTemplate, events: { 'click .search-load-next': 'loadNext', 'click .search-back-to-courses': 'backToCourses' }, - SearchItemView: DashSearchItemView, backToCourses: function() { this.clear(); @@ -26,4 +28,4 @@ }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/base/views/search_form.js b/openedx/features/course_search/static/course_search/js/views/search_form.js similarity index 85% rename from lms/static/js/search/base/views/search_form.js rename to openedx/features/course_search/static/course_search/js/views/search_form.js index c9d6153883f3..5ad29ff923c2 100644 --- a/lms/static/js/search/base/views/search_form.js +++ b/openedx/features/course_search/static/course_search/js/views/search_form.js @@ -1,7 +1,7 @@ (function(define) { - define(['jquery', 'backbone'], function($, Backbone) { - 'use strict'; + 'use strict'; + define(['jquery', 'backbone'], function($, Backbone) { return Backbone.View.extend({ el: '', @@ -22,19 +22,17 @@ }, doSearch: function(term) { + var trimmed; if (term) { - this.$searchField.val(term); - } - else { - term = this.$searchField.val(); + trimmed = term.trim(); + this.$searchField.val(trimmed); + } else { + trimmed = this.$searchField.val().trim(); } - - var trimmed = $.trim(term); if (trimmed) { this.setActiveStyle(); this.trigger('search', trimmed); - } - else { + } else { this.clearSearch(); } }, @@ -63,4 +61,4 @@ }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/base/views/search_item_view.js b/openedx/features/course_search/static/course_search/js/views/search_item_view.js similarity index 68% rename from lms/static/js/search/base/views/search_item_view.js rename to openedx/features/course_search/static/course_search/js/views/search_item_view.js index 8d406a036815..75b0f7bd928e 100644 --- a/lms/static/js/search/base/views/search_item_view.js +++ b/openedx/features/course_search/static/course_search/js/views/search_item_view.js @@ -1,40 +1,41 @@ (function(define) { + 'use strict'; + define([ 'jquery', 'underscore', 'backbone', 'gettext', - 'logger' - ], function($, _, Backbone, gettext, Logger) { - 'use strict'; - + 'logger', + 'edx-ui-toolkit/js/utils/html-utils' + ], function($, _, Backbone, gettext, Logger, HtmlUtils) { return Backbone.View.extend({ tagName: 'li', - templateId: '', className: 'search-results-item', attributes: { - 'role': 'region', + role: 'region', 'aria-label': 'search result' }, events: { - 'click': 'logSearchItem' + click: 'logSearchItem' }, - initialize: function() { - this.tpl = _.template($(this.templateId).html()); + initialize: function(options) { + this.template = options.template; }, render: function() { var data = _.clone(this.model.attributes); - // Drop the preview text and result type if the search term is found - // in the title/location in the course hierarchy + + // Drop the preview text and result type if the search term is found + // in the title/location in the course hierarchy if (this.model.get('content_type') === 'Sequence') { data.excerpt = ''; data.content_type = ''; } - this.$el.html(this.tpl(data)); + HtmlUtils.setHtml(this.$el, HtmlUtils.template(this.template)(data)); return this; }, @@ -47,7 +48,6 @@ }, logSearchItem: function(event) { - event.preventDefault(); var self = this; var target = this.model.id; var link = this.model.get('url'); @@ -56,10 +56,13 @@ var pageSize = collection.pageSize; var searchTerm = collection.searchTerm; var index = collection.indexOf(this.model); + + event.preventDefault(); + Logger.log('edx.course.search.result_selected', { - 'search_term': searchTerm, - 'result_position': (page * pageSize + index), - 'result_link': target + search_term: searchTerm, + result_position: (page * pageSize) + index, + result_link: target }).always(function() { self.redirect(link); }); @@ -67,4 +70,4 @@ }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/static/js/search/base/views/search_results_view.js b/openedx/features/course_search/static/course_search/js/views/search_results_view.js similarity index 57% rename from lms/static/js/search/base/views/search_results_view.js rename to openedx/features/course_search/static/course_search/js/views/search_results_view.js index 55e31219adab..b568d7a1d2a6 100644 --- a/lms/static/js/search/base/views/search_results_view.js +++ b/openedx/features/course_search/static/course_search/js/views/search_results_view.js @@ -1,33 +1,34 @@ (function(define) { + 'use strict'; + define([ 'jquery', 'underscore', 'backbone', - 'gettext' - ], function($, _, Backbone, gettext) { - 'use strict'; - + 'edx-ui-toolkit/js/utils/html-utils', + 'edx-ui-toolkit/js/utils/string-utils', + 'course_search/js/views/search_item_view', + 'text!course_search/templates/search_loading.underscore', + 'text!course_search/templates/search_error.underscore' + ], function($, _, Backbone, HtmlUtils, StringUtils, SearchItemView, searchLoadingTemplate, searchErrorTemplate) { return Backbone.View.extend({ - // these should be defined by subclasses + // these should be defined by subclasses el: '', contentElement: '', - resultsTemplateId: '', - loadingTemplateId: '', - errorTemplateId: '', + resultsTemplate: null, + itemTemplate: null, + loadingTemplate: searchLoadingTemplate, + errorTemplate: searchErrorTemplate, events: {}, spinner: '.search-load-next .icon', - SearchItemView: function() {}, initialize: function() { this.$contentElement = $(this.contentElement); - this.resultsTemplate = _.template($(this.resultsTemplateId).html()); - this.loadingTemplate = _.template($(this.loadingTemplateId).html()); - this.errorTemplate = _.template($(this.errorTemplateId).html()); }, render: function() { - this.$el.html(this.resultsTemplate({ + HtmlUtils.setHtml(this.$el, HtmlUtils.template(this.resultsTemplate)({ totalCount: this.collection.totalCount, totalCountMsg: this.totalCountMsg(), pageSize: this.collection.pageSize, @@ -40,10 +41,10 @@ }, renderNext: function() { - // total count may have changed + // total count may have changed this.$el.find('.search-count').text(this.totalCountMsg()); this.renderItems(); - if (! this.collection.hasNextPage()) { + if (!this.collection.hasNextPage()) { this.$el.find('.search-load-next').remove(); } this.$el.find(this.spinner).hide(); @@ -52,15 +53,20 @@ renderItems: function() { var latest = this.collection.latestModels(); var items = latest.map(function(result) { - var item = new this.SearchItemView({model: result}); + var item = new SearchItemView({ + model: result, + template: this.itemTemplate + }); return item.render().el; }, this); this.$el.find('ol').append(items); }, totalCountMsg: function() { - var fmt = ngettext('%s result', '%s results', this.collection.totalCount); - return interpolate(fmt, [this.collection.totalCount]); + var fmt = ngettext('{total_results} result', '{total_results} results', this.collection.totalCount); + return StringUtils.interpolate(fmt, { + total_results: this.collection.totalCount + }); }, clear: function() { @@ -75,26 +81,28 @@ showLoadingMessage: function() { this.doCleanup(); - this.$el.html(this.loadingTemplate()); + HtmlUtils.setHtml(this.$el, HtmlUtils.template(this.loadingTemplate)()); this.showResults(); }, showErrorMessage: function() { - this.$el.html(this.errorTemplate()); + HtmlUtils.setHtml(this.$el, HtmlUtils.template(this.errorTemplate)()); this.showResults(); }, doCleanup: function() { - // Empty any loading/error message and empty the el - // Bookmarks share the same container element, So we are doing - // this to ensure that elements are in clean/initial state + // Empty any loading/error message and empty the el + // Bookmarks share the same container element, So we are doing + // this to ensure that elements are in clean/initial state $('#loading-message').html(''); $('#error-message').html(''); this.$el.html(''); }, loadNext: function(event) { - event && event.preventDefault(); + if (event) { + event.preventDefault(); + } this.$el.find(this.spinner).show(); this.trigger('next'); return false; @@ -102,4 +110,4 @@ }); }); -})(define || RequireJS.define); +}(define || RequireJS.define)); diff --git a/lms/templates/search/course_search_item.underscore b/openedx/features/course_search/static/course_search/templates/course_search_item.underscore similarity index 100% rename from lms/templates/search/course_search_item.underscore rename to openedx/features/course_search/static/course_search/templates/course_search_item.underscore diff --git a/lms/templates/search/course_search_results.underscore b/openedx/features/course_search/static/course_search/templates/course_search_results.underscore similarity index 100% rename from lms/templates/search/course_search_results.underscore rename to openedx/features/course_search/static/course_search/templates/course_search_results.underscore diff --git a/lms/templates/search/dashboard_search_item.underscore b/openedx/features/course_search/static/course_search/templates/dashboard_search_item.underscore similarity index 100% rename from lms/templates/search/dashboard_search_item.underscore rename to openedx/features/course_search/static/course_search/templates/dashboard_search_item.underscore diff --git a/lms/templates/search/dashboard_search_results.underscore b/openedx/features/course_search/static/course_search/templates/dashboard_search_results.underscore similarity index 100% rename from lms/templates/search/dashboard_search_results.underscore rename to openedx/features/course_search/static/course_search/templates/dashboard_search_results.underscore diff --git a/lms/templates/search/search_error.underscore b/openedx/features/course_search/static/course_search/templates/search_error.underscore similarity index 100% rename from lms/templates/search/search_error.underscore rename to openedx/features/course_search/static/course_search/templates/search_error.underscore diff --git a/lms/templates/search/search_loading.underscore b/openedx/features/course_search/static/course_search/templates/search_loading.underscore similarity index 100% rename from lms/templates/search/search_loading.underscore rename to openedx/features/course_search/static/course_search/templates/search_loading.underscore diff --git a/openedx/features/course_search/views/__init__.py b/openedx/features/course_search/views/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/themes/edx.org/lms/templates/dashboard.html b/themes/edx.org/lms/templates/dashboard.html index 2e7733a2be82..91f7f56856eb 100644 --- a/themes/edx.org/lms/templates/dashboard.html +++ b/themes/edx.org/lms/templates/dashboard.html @@ -29,12 +29,6 @@ <%static:include path="dashboard/${template_name}.underscore" /> % endfor - -% for template_name in ["dashboard_search_item", "dashboard_search_results", "search_loading", "search_error"]: - -% endfor <%block name="js_extra"> @@ -50,7 +44,7 @@ }); % if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'): - <%static:require_module module_name="js/search/dashboard/dashboard_search_factory" class_name="DashboardSearchFactory"> + <%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory"> DashboardSearchFactory(); % endif