diff --git a/common/static/common/js/discussion/views/discussion_thread_list_view.js b/common/static/common/js/discussion/views/discussion_thread_list_view.js index 5ac5c91295..7ab85715d6 100644 --- a/common/static/common/js/discussion/views/discussion_thread_list_view.js +++ b/common/static/common/js/discussion/views/discussion_thread_list_view.js @@ -1,6 +1,7 @@ -/* globals _, Backbone, Content, Discussion, DiscussionUtil */ +/* globals _, Backbone, Content, Discussion, DiscussionUtil, DiscussionThreadView, DiscussionThreadListView */ (function() { 'use strict'; + /* eslint-disable */ var __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { @@ -17,6 +18,7 @@ child.__super__ = parent.prototype; return child; }; + /* eslint-enable */ if (typeof Backbone !== 'undefined' && Backbone !== null) { this.DiscussionThreadListView = (function(_super) { @@ -36,24 +38,6 @@ this.chooseFilter = function() { return DiscussionThreadListView.prototype.chooseFilter.apply(self, arguments); }; - this.keyboardBinding = function() { - return DiscussionThreadListView.prototype.keyboardBinding.apply(self, arguments); - }; - this.filterTopics = function() { - return DiscussionThreadListView.prototype.filterTopics.apply(self, arguments); - }; - this.toggleBrowseMenu = function() { - return DiscussionThreadListView.prototype.toggleBrowseMenu.apply(self, arguments); - }; - this.hideBrowseMenu = function() { - return DiscussionThreadListView.prototype.hideBrowseMenu.apply(self, arguments); - }; - this.showBrowseMenu = function() { - return DiscussionThreadListView.prototype.showBrowseMenu.apply(self, arguments); - }; - this.isBrowseMenuVisible = function() { - return DiscussionThreadListView.prototype.isBrowseMenuVisible.apply(self, arguments); - }; this.threadRemoved = function() { return DiscussionThreadListView.prototype.threadRemoved.apply(self, arguments); }; @@ -72,9 +56,6 @@ this.renderThreads = function() { return DiscussionThreadListView.prototype.renderThreads.apply(self, arguments); }; - this.updateSidebar = function() { - return DiscussionThreadListView.prototype.updateSidebar.apply(self, arguments); - }; this.addAndSelectThread = function() { return DiscussionThreadListView.prototype.addAndSelectThread.apply(self, arguments); }; @@ -90,17 +71,16 @@ this.addSearchAlert = function() { return DiscussionThreadListView.prototype.addSearchAlert.apply(self, arguments); }; - return DiscussionThreadListView.__super__.constructor.apply(this, arguments); + this.performSearch = function() { + return DiscussionThreadListView.prototype.performSearch.apply(self, arguments); + }; + return DiscussionThreadListView.__super__.constructor.apply(this, arguments); // eslint-disable-line no-underscore-dangle, max-len } DiscussionThreadListView.prototype.events = { 'keypress .forum-nav-browse-filter-input': function(event) { return DiscussionUtil.ignoreEnterKey(event); }, - 'keyup .forum-nav-browse-filter-input': 'filterTopics', - 'keydown .forum-nav-browse-filter-input': 'keyboardBinding', - 'click .forum-nav-browse-menu-wrapper': 'ignoreClick', - 'click .forum-nav-browse-title': 'selectTopicHandler', 'change .forum-nav-sort-control': 'sortThreads', 'click .forum-nav-thread-link': 'threadSelected', 'click .forum-nav-load-more-link': 'loadMorePages', @@ -117,8 +97,6 @@ this.collection.on('change', this.reloadDisplayedCollection); this.discussionIds = ''; this.collection.on('reset', function(discussion) { - var board; - board = $('.current-board').html(); self.displayedCollection.current_page = discussion.current_page; self.displayedCollection.pages = discussion.pages; return self.displayedCollection.reset(discussion.models); @@ -129,23 +107,15 @@ this.boardName = null; this.current_search = ''; this.mode = 'all'; - this.keyCodes = { - enter: 13, - escape: 27, - up: 38, - down: 40 - }; - this.filterInputReset(); - this.selectedTopic = $('.forum-nav-browse-menu-item:visible .forum-nav-browse-title.is-focused'); this.searchAlertCollection = new Backbone.Collection([], { model: Backbone.Model }); this.searchAlertCollection.on('add', function(searchAlert) { var content; content = edx.HtmlUtils.template($('#search-alert-template').html())({ - 'messageHtml': searchAlert.attributes.message, - 'cid': searchAlert.cid, - 'css_class': searchAlert.attributes.css_class + messageHtml: searchAlert.attributes.message, + cid: searchAlert.cid, + css_class: searchAlert.attributes.css_class }); edx.HtmlUtils.append(self.$('.search-alerts'), content); return self.$('#search-alert-' + searchAlert.cid + ' .dismiss') @@ -160,7 +130,6 @@ return self.$('.search-alerts').empty(); }); this.template = edx.HtmlUtils.template($('#thread-list-template').html()); - this.homeTemplate = edx.HtmlUtils.template($('#discussion-home-template').html()); this.threadListItemTemplate = edx.HtmlUtils.template($('#thread-list-item-template').html()); }; @@ -172,10 +141,9 @@ * @returns {Backbone.Model} */ DiscussionThreadListView.prototype.addSearchAlert = function(message, cssClass) { - var m; - m = new Backbone.Model({message: message, css_class: cssClass || ''}); - this.searchAlertCollection.add(m); - return m; + var searchAlertModel = new Backbone.Model({message: message, css_class: cssClass || ''}); + this.searchAlertCollection.add(searchAlertModel); + return searchAlertModel; }; DiscussionThreadListView.prototype.removeSearchAlert = function(searchAlert) { @@ -196,7 +164,7 @@ $currentElement.replaceWith($content); this.showMetadataAccordingToSort(); if (active) { - return this.setActiveThread(threadId); + this.setActiveThread(threadId); } }; @@ -212,37 +180,6 @@ }); }; - DiscussionThreadListView.prototype.updateSidebar = function() { - var amount, browseFilterHeight, discussionBody, discussionBottomOffset, discussionsBodyBottom, - discussionsBodyTop, headerHeight, refineBarHeight, scrollTop, sidebar, sidebarHeight, topOffset, - windowHeight; - scrollTop = $(window).scrollTop(); - windowHeight = $(window).height(); - discussionBody = $('.discussion-column'); - discussionsBodyTop = discussionBody[0] ? discussionBody.offset().top : void 0; - discussionsBodyBottom = discussionsBodyTop + discussionBody.outerHeight(); - sidebar = $('.forum-nav'); - if (scrollTop > discussionsBodyTop - this.sidebar_padding) { - sidebar.css('top', scrollTop - discussionsBodyTop + this.sidebar_padding); - } else { - sidebar.css('top', '0'); - } - sidebarHeight = windowHeight - Math.max(discussionsBodyTop - scrollTop, this.sidebar_padding); - topOffset = scrollTop + windowHeight; - discussionBottomOffset = discussionsBodyBottom + this.sidebar_padding; - amount = Math.max(topOffset - discussionBottomOffset, 0); - sidebarHeight = sidebarHeight - this.sidebar_padding - amount; - sidebarHeight = Math.min(sidebarHeight + 1, discussionBody.outerHeight()); - sidebar.css('height', sidebarHeight); - headerHeight = this.$('.forum-nav-header').outerHeight(); - refineBarHeight = this.$('.forum-nav-refine-bar').outerHeight(); - browseFilterHeight = this.$('.forum-nav-browse-filter').outerHeight(); - this.$('.forum-nav-thread-list') - .css('height', (sidebarHeight - headerHeight - refineBarHeight - 2) + 'px'); - this.$('.forum-nav-browse-menu') - .css('height', (sidebarHeight - headerHeight - browseFilterHeight - 2) + 'px'); - }; - DiscussionThreadListView.prototype.ignoreClick = function(event) { return event.stopPropagation(); }; @@ -261,16 +198,14 @@ this.$('.forum-nav-sort-control option').removeProp('selected'); this.$('.forum-nav-sort-control option[value=' + this.collection.sort_preference + ']') .prop('selected', true); - $(window).bind('load scroll resize', this.updateSidebar); this.displayedCollection.on('reset', this.renderThreads); this.displayedCollection.on('thread:remove', this.renderThreads); this.displayedCollection.on('change:commentable_id', function() { if (self.mode === 'commentables') { - return self.retrieveDiscussions(self.discussionIds.split(',')); + self.retrieveDiscussions(self.discussionIds.split(',')); } }); this.renderThreads(); - this.showBrowseMenu(true); return this; }; @@ -284,7 +219,6 @@ } this.showMetadataAccordingToSort(); this.renderMorePages(); - this.updateSidebar(); this.trigger('threads:rendered'); }; @@ -319,7 +253,7 @@ }; DiscussionThreadListView.prototype.loadMorePages = function(event) { - var error, lastThread, loadMoreElem, loadingElem, options, _ref, + var error, lastThread, loadMoreElem, loadingElem, options, ref, self = this; if (event) { event.preventDefault(); @@ -353,9 +287,11 @@ if (this.group_id) { options.group_id = this.group_id; } + break; + default: } - _ref = this.collection.last(); - lastThread = _ref ? _ref.get('id') : void 0; + ref = this.collection.last(); + lastThread = ref ? ref.get('id') : void 0; if (lastThread) { this.once('threads:rendered', function() { var classSelector = @@ -365,8 +301,8 @@ }); } else { this.once('threads:rendered', function() { - var _ref1 = $('.forum-nav-thread-link').first(); - return _ref1 ? _ref1.focus() : void 0; + var ref1 = $('.forum-nav-thread-link').first(); + return ref1 ? ref1.focus() : void 0; }); } error = function() { @@ -421,224 +357,10 @@ .prepend($srElem); }; - DiscussionThreadListView.prototype.goHome = function() { - var url, $templateContent; - $templateContent = $(this.homeTemplate().toString()); - $('.forum-content').empty().append($templateContent); - $('.forum-nav-thread-list a').removeClass('is-active').find('.sr') - .remove(); - $('input.email-setting').bind('click', this.updateEmailNotifications); - url = DiscussionUtil.urlFor('notifications_status', window.user.get('id')); - DiscussionUtil.safeAjax({ - url: url, - type: 'GET', - success: function(response) { - $('input.email-setting').prop('checked', response.status); - } - }); - }; - - DiscussionThreadListView.prototype.isBrowseMenuVisible = function() { - return this.$('.forum-nav-browse-menu-wrapper').is(':visible'); - }; - - DiscussionThreadListView.prototype.showBrowseMenu = function(initialLoad) { - if (!this.isBrowseMenuVisible()) { - this.$('.forum-nav-browse-menu-wrapper').show(); - this.$('.forum-nav-thread-list-wrapper').hide(); - if (!initialLoad) { - $('.forum-nav-browse-filter-input').focus(); - this.filterInputReset(); - } - $('body').bind('click', this.hideBrowseMenu); - return this.updateSidebar(); - } - }; - - DiscussionThreadListView.prototype.hideBrowseMenu = function() { - var selectedTopicList = this.$('.forum-nav-browse-title.is-focused'); - if (this.isBrowseMenuVisible()) { - selectedTopicList.removeClass('is-focused'); - this.$('.forum-nav-browse-menu-wrapper').hide(); - this.$('.forum-nav-thread-list-wrapper').show(); - if (this.selectedTopicId !== 'undefined') { - this.$('.forum-nav-browse-filter-input').attr('aria-activedescendant', this.selectedTopicId); - } - $('body').unbind('click', this.hideBrowseMenu); - return this.updateSidebar(); - } - }; - - DiscussionThreadListView.prototype.toggleBrowseMenu = function(event) { - var inputText = $('.forum-nav-browse-filter-input').val(); - event.preventDefault(); - event.stopPropagation(); - if (this.isBrowseMenuVisible()) { - return this.hideBrowseMenu(); - } else { - if (inputText !== '') { - this.filterTopics(inputText); - } - return this.showBrowseMenu(); - } - }; - - DiscussionThreadListView.prototype.getPathText = function(item) { - var path, pathTitles; - path = item.parents('.forum-nav-browse-menu-item').andSelf(); - pathTitles = path.children('.forum-nav-browse-title').map(function(i, elem) { - return $(elem).text(); - }).get(); - return pathTitles.join(' / '); - }; - - DiscussionThreadListView.prototype.getBreadcrumbText = function($item) { - var $parentSubMenus = $item.parents('.forum-nav-browse-submenu'), - crumbs = [], - subTopic = $('.forum-nav-browse-title', $item) - .first() - .text() - .trim(); - - $parentSubMenus.each(function(i, el) { - crumbs.push($(el).siblings('.forum-nav-browse-title') - .first() - .text() - .trim() - ); - }); - - if (subTopic !== 'All Discussions') { - crumbs.push(subTopic); - } - - return crumbs; - }; - - DiscussionThreadListView.prototype.selectOption = function(element) { - var activeDescendantId, activeDescendantText; - if (this.selectedTopic.length > 0) { - this.selectedTopic.removeClass('is-focused'); - } - if (element) { - element.addClass('is-focused'); - activeDescendantId = element.parent().attr('id'); - activeDescendantText = element.text(); - this.selectedTopic = element; - this.selectedTopicId = activeDescendantId; - $('.forum-nav-browse-filter-input') - .attr('aria-activedescendant', activeDescendantId) - .val(activeDescendantText); - } - }; - - DiscussionThreadListView.prototype.filterInputReset = function() { - this.filterEnabled = true; - this.selectedTopicIndex = -1; - this.selectedTopicId = null; - }; - - DiscussionThreadListView.prototype.keyboardBinding = function(event) { - var $inputText = $('.forum-nav-browse-filter-input'), - $filteredMenuItems = $('.forum-nav-browse-menu-item:visible'), - filteredMenuItemsLen = $filteredMenuItems.length, - $curOption = $filteredMenuItems.eq(0).find('.forum-nav-browse-title').eq(0), - $activeOption, $prev, $next; - - switch (event.keyCode) { - case this.keyCodes.enter: - $activeOption = $filteredMenuItems.find('.forum-nav-browse-title.is-focused'); - if ($inputText.val() !== '') { - $activeOption.trigger('click'); - this.filterInputReset(); - } - break; - - case this.keyCodes.escape: - this.toggleBrowseMenu(event); - $('.forum-nav-browse-filter-input').val(''); - this.filterInputReset(); - $('.all-topics').trigger('click'); - break; - - case this.keyCodes.up: - if (this.selectedTopicIndex > 0) { - this.selectedTopicIndex -= 1; - if (this.isBrowseMenuVisible()) { - $prev = $('.forum-nav-browse-menu-item:visible') - .eq(this.selectedTopicIndex).find('.forum-nav-browse-title') - .eq(0); - this.filterEnabled = false; - $curOption.removeClass('is-focused'); - $prev.addClass('is-focused'); - } - this.selectOption($prev); - } - break; - - case this.keyCodes.down: - if (this.selectedTopicIndex < filteredMenuItemsLen - 1) { - this.selectedTopicIndex += 1; - if (this.isBrowseMenuVisible()) { - $next = $('.forum-nav-browse-menu-item:visible') - .eq(this.selectedTopicIndex).find('.forum-nav-browse-title') - .eq(0); - this.filterEnabled = false; - $curOption.removeClass('is-focused'); - $next.addClass('is-focused'); - } - this.selectOption($next); - } - break; - - default: - break; - } - return true; - }; - - DiscussionThreadListView.prototype.filterTopics = function() { - var items, query, filteredItems, - self = this; - query = this.$('.forum-nav-browse-filter-input').val(); - items = this.$('.forum-nav-browse-menu-item'); - if (query.length === 0) { - items.find('.forum-nav-browse-title.is-focused').removeClass('is-focused'); - return items.show(); - } else { - if (this.filterEnabled) { - items.hide(); - filteredItems = items.each(function(i, item) { - var path, pathText, - $item = $(item); - if (!$item.is(':visible')) { - pathText = self.getPathText($item).toLowerCase(); - if (query.split(' ').every(function(term) { - return pathText.search(term.toLowerCase()) !== -1; - })) { - path = $item.parents('.forum-nav-browse-menu-item').andSelf(); - return path.add($item.find('.forum-nav-browse-menu-item')).show(); - } - } - return filteredItems; - }); - } - return filteredItems; - } - }; - - DiscussionThreadListView.prototype.selectTopicHandler = function(event) { - event.preventDefault(); - return this.selectTopic($(event.target)); - }; - DiscussionThreadListView.prototype.selectTopic = function($target) { var allItems, discussionIds, $item; - this.hideBrowseMenu(); $item = $target.closest('.forum-nav-browse-menu-item'); - this.trigger('topic:selected', this.getBreadcrumbText($item)); - if ($item.hasClass('forum-nav-browse-menu-all')) { this.discussionIds = ''; this.$('.forum-nav-filter-cohort').show(); @@ -667,9 +389,8 @@ }; DiscussionThreadListView.prototype.retrieveDiscussion = function(discussionId, callback) { - var url, + var url = DiscussionUtil.urlFor('retrieve_discussion', discussionId), self = this; - url = DiscussionUtil.urlFor('retrieve_discussion', discussionId); return DiscussionUtil.safeAjax({ url: url, type: 'GET', @@ -686,8 +407,8 @@ }); }; - DiscussionThreadListView.prototype.retrieveDiscussions = function(discussion_ids) { - this.discussionIds = discussion_ids.join(','); + DiscussionThreadListView.prototype.retrieveDiscussions = function(discussionIds) { + this.discussionIds = discussionIds.join(','); this.mode = 'commentables'; return this.retrieveFirstPage(); }; @@ -709,19 +430,18 @@ }; DiscussionThreadListView.prototype.performSearch = function($searchInput) { - this.hideBrowseMenu(); // trigger this event so the breadcrumbs can update as well this.trigger('search:initiated'); this.searchFor($searchInput.val(), $searchInput); }; DiscussionThreadListView.prototype.searchFor = function(text, $searchInput) { - var url, self = this; + var url = DiscussionUtil.urlFor('search'), + self = this; this.clearSearchAlerts(); this.clearFilters(); this.mode = 'search'; this.current_search = text; - url = DiscussionUtil.urlFor('search'); /* TODO: This might be better done by setting discussion.current_page=0 and calling discussion.loadMorePages @@ -789,6 +509,7 @@ return self.searchForUser(text); } } + return response; } }); }; @@ -816,9 +537,9 @@ ); message = edx.HtmlUtils.interpolateHtml( - gettext('Show posts by {username}.'), {'username': username} + gettext('Show posts by {username}.'), {username: username} ); - return self.addSearchAlert(message, 'search-by-user'); + self.addSearchAlert(message, 'search-by-user'); } } }); diff --git a/common/static/common/js/spec/discussion/view/discussion_thread_edit_view_spec.js b/common/static/common/js/spec/discussion/view/discussion_thread_edit_view_spec.js index c431ae1b2f..abfe539bfd 100644 --- a/common/static/common/js/spec/discussion/view/discussion_thread_edit_view_spec.js +++ b/common/static/common/js/spec/discussion/view/discussion_thread_edit_view_spec.js @@ -16,7 +16,7 @@ 'title': 'test thread title' }); this.thread = new Thread(this.threadData); - this.course_settings = DiscussionSpecHelper.makeCourseSettings(); + this.course_settings = DiscussionSpecHelper.createTestCourseSettings(); this.createEditView = function(options) { options = _.extend({ diff --git a/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js b/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js index 7ccf8018fa..b142c6b784 100644 --- a/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js +++ b/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js @@ -266,6 +266,88 @@ changeSorting(this.threads, 'votes', 'comments', ['Thread1', 'Thread4', 'Thread3', 'Thread2']); }); }); + + describe('post type renders correctly', function() { + it('for discussion', function() { + renderSingleThreadWithProps({ + thread_type: 'discussion' + }); + expect($('.forum-nav-thread-wrapper-0 .icon')).toHaveClass('fa-comments'); + return expect($('.forum-nav-thread-wrapper-0 .sr')).toHaveText('discussion'); + }); + + it('for answered question', function() { + renderSingleThreadWithProps({ + thread_type: 'question', + endorsed: true + }); + expect($('.forum-nav-thread-wrapper-0 .icon')).toHaveClass('fa-check-square-o'); + return expect($('.forum-nav-thread-wrapper-0 .sr')).toHaveText('answered question'); + }); + + it('for unanswered question', function() { + renderSingleThreadWithProps({ + thread_type: 'question', + endorsed: false + }); + expect($('.forum-nav-thread-wrapper-0 .icon')).toHaveClass('fa-question'); + return expect($('.forum-nav-thread-wrapper-0 .sr')).toHaveText('unanswered question'); + }); + }); + + describe('post labels render correctly', function() { + beforeEach(function() { + this.moderatorId = '42'; + this.administratorId = '43'; + this.communityTaId = '44'; + return DiscussionUtil.loadRoles({ + Moderator: [parseInt(this.moderatorId, 10)], + Administrator: [parseInt(this.administratorId, 10)], + 'Community TA': [parseInt(this.communityTaId, 10)] + }); + }); + + it('for pinned', function() { + renderSingleThreadWithProps({ + pinned: true + }); + return expect($('.post-label-pinned').length).toEqual(1); + }); + + it('for following', function() { + renderSingleThreadWithProps({ + subscribed: true + }); + return expect($('.post-label-following').length).toEqual(1); + }); + + it('for moderator', function() { + renderSingleThreadWithProps({ + user_id: this.moderatorId + }); + return expect($('.post-label-by-staff').length).toEqual(1); + }); + + it('for administrator', function() { + renderSingleThreadWithProps({ + user_id: this.administratorId + }); + return expect($('.post-label-by-staff').length).toEqual(1); + }); + + it('for community TA', function() { + renderSingleThreadWithProps({ + user_id: this.communityTaId + }); + return expect($('.post-label-by-community-ta').length).toEqual(1); + }); + + it('when none should be present', function() { + renderSingleThreadWithProps({}); + return expect($('.forum-nav-thread-labels').length).toEqual(0); + }); + }); + describe('search alerts', function() { var testAlertMessages, getAlertMessagesAndClasses; @@ -460,228 +542,5 @@ }); }); - describe('post type renders correctly', function() { - it('for discussion', function() { - renderSingleThreadWithProps({ - thread_type: 'discussion' - }); - expect($('.forum-nav-thread-wrapper-0 .icon')).toHaveClass('fa-comments'); - return expect($('.forum-nav-thread-wrapper-0 .sr')).toHaveText('discussion'); - }); - - it('for answered question', function() { - renderSingleThreadWithProps({ - thread_type: 'question', - endorsed: true - }); - expect($('.forum-nav-thread-wrapper-0 .icon')).toHaveClass('fa-check-square-o'); - return expect($('.forum-nav-thread-wrapper-0 .sr')).toHaveText('answered question'); - }); - - it('for unanswered question', function() { - renderSingleThreadWithProps({ - thread_type: 'question', - endorsed: false - }); - expect($('.forum-nav-thread-wrapper-0 .icon')).toHaveClass('fa-question'); - return expect($('.forum-nav-thread-wrapper-0 .sr')).toHaveText('unanswered question'); - }); - }); - - describe('post labels render correctly', function() { - beforeEach(function() { - this.moderatorId = '42'; - this.administratorId = '43'; - this.communityTaId = '44'; - return DiscussionUtil.loadRoles({ - 'Moderator': [parseInt(this.moderatorId)], - 'Administrator': [parseInt(this.administratorId)], - 'Community TA': [parseInt(this.communityTaId)] - }); - }); - - it('for pinned', function() { - renderSingleThreadWithProps({ - pinned: true - }); - return expect($('.post-label-pinned').length).toEqual(1); - }); - - it('for following', function() { - renderSingleThreadWithProps({ - subscribed: true - }); - return expect($('.post-label-following').length).toEqual(1); - }); - - it('for moderator', function() { - renderSingleThreadWithProps({ - user_id: this.moderatorId - }); - return expect($('.post-label-by-staff').length).toEqual(1); - }); - - it('for administrator', function() { - renderSingleThreadWithProps({ - user_id: this.administratorId - }); - return expect($('.post-label-by-staff').length).toEqual(1); - }); - - it('for community TA', function() { - renderSingleThreadWithProps({ - user_id: this.communityTaId - }); - return expect($('.post-label-by-community-ta').length).toEqual(1); - }); - - it('when none should be present', function() { - renderSingleThreadWithProps({}); - return expect($('.forum-nav-thread-labels').length).toEqual(0); - }); - }); - - describe('browse menu', function() { - var expectBrowseMenuVisible; - afterEach(function() { - return $('body').unbind('click'); - }); - - expectBrowseMenuVisible = function(isVisible) { - expect($('.forum-nav-browse-menu:visible').length).toEqual(isVisible ? 1 : 0); - return expect($('.forum-nav-thread-list-wrapper:visible').length).toEqual(isVisible ? 0 : 1); - }; - - it('should be visible by default', function() { - expectBrowseMenuVisible(true); - }); - - it('should disappear when header button is clicked', function() { - $('.forum-nav-browse').click(); - return expectBrowseMenuVisible(false); - }); - - describe('when shown', function() { - it('should show again when header button is clicked', function() { - $('.forum-nav-browse').click(); - return expectBrowseMenuVisible(false); - }); - - it('should hide when a click outside the menu occurs', function() { - $('.forum-nav-search-input').click(); - return expectBrowseMenuVisible(false); - }); - - it('should hide when a category is clicked', function() { - $('.forum-nav-browse-title')[0].click(); - return expectBrowseMenuVisible(false); - }); - - it('should still be shown when filter input is clicked', function() { - $('.forum-nav-browse-filter-input').click(); - return expectBrowseMenuVisible(true); - }); - - describe('filtering', function() { - var checkFilter; - checkFilter = function(filterText, expectedItems) { - var visibleItems; - $('.forum-nav-browse-filter-input').val(filterText).keyup(); - visibleItems = $('.forum-nav-browse-title:visible').map(function(i, elem) { - return $(elem).text(); - }).get(); - return expect(visibleItems).toEqual(expectedItems); - }; - - it('should be case-insensitive', function() { - return checkFilter('other', ['Other Category']); - }); - - it('should match partial words', function() { - return checkFilter('ateg', ['Other Category']); - }); - - it('should show ancestors and descendants of matches', function() { - return checkFilter('Target', ['Parent', 'Target', 'Child']); - }); - - it('should handle multiple words regardless of order', function() { - return checkFilter('Following Posts', ["Posts I'm Following"]); - }); - - it('should handle multiple words in different depths', function() { - return checkFilter('Parent Child', ['Parent', 'Target', 'Child']); - }); - }); - }); - - describe('selecting an item', function() { - var testSelectionRequest; - - it('should show/hide the cohort selector', function() { - var self = this; - DiscussionSpecHelper.makeModerator(); - this.view.render(); - setupAjax(); - return _.each([ - { - selector: '.forum-nav-browse-menu-all', - cohortVisibility: true - }, { - selector: '.forum-nav-browse-menu-following', - cohortVisibility: false - }, { - selector: '.forum-nav-browse-menu-item:' + - 'has(.forum-nav-browse-menu-item .forum-nav-browse-menu-item)', - cohortVisibility: false - }, { - selector: '[data-discussion-id=child]', - cohortVisibility: false - }, { - selector: '[data-discussion-id=other]', - cohortVisibility: true - } - ], function(itemInfo) { - self.view.$('' + itemInfo.selector + ' > .forum-nav-browse-title').click(); - return expect(self.view.$('.forum-nav-filter-cohort').is(':visible')) - .toEqual(itemInfo.cohortVisibility); - }); - }); - - testSelectionRequest = function(callback, itemText) { - setupAjax(callback); - $('.forum-nav-browse-title:contains(' + itemText + ')').click(); - return expect($.ajax).toHaveBeenCalled(); - }; - - it('should get all discussions', function() { - return testSelectionRequest(function(params) { - return expect(params.url.path()).toEqual(DiscussionUtil.urlFor('threads')); - }, 'All'); - }); - - it('should get followed threads', function() { - testSelectionRequest(function(params) { - return expect(params.url.path()) - .toEqual(DiscussionUtil.urlFor('followed_threads', window.user.id)); - }, 'Following'); - return expect($.ajax.calls.mostRecent().args[0].data.group_id).toBeUndefined(); - }); - - it('should get threads for the selected leaf', function() { - return testSelectionRequest(function(params) { - expect(params.url.path()).toEqual(DiscussionUtil.urlFor('search')); - return expect(params.data.commentable_ids).toEqual('child'); - }, 'Child'); - }); - - it('should get threads for children of the selected intermediate node', function() { - return testSelectionRequest(function(params) { - expect(params.url.path()).toEqual(DiscussionUtil.urlFor('search')); - return expect(params.data.commentable_ids).toEqual('child,sibling'); - }, 'Parent'); - }); - }); - }); }); }).call(this); diff --git a/common/static/common/js/spec/discussion/view/discussion_thread_show_view_spec.js b/common/static/common/js/spec/discussion/view/discussion_thread_show_view_spec.js index b13c9d6c23..fe9ac3332e 100644 --- a/common/static/common/js/spec/discussion/view/discussion_thread_show_view_spec.js +++ b/common/static/common/js/spec/discussion/view/discussion_thread_show_view_spec.js @@ -5,7 +5,7 @@ var $$course_id = '$$course_id'; describe('DiscussionThreadShowView', function() { beforeEach(function() { - DiscussionSpecHelper.setUpGlobals(); + DiscussionSpecHelper.setUpGlobals({}); DiscussionSpecHelper.setUnderscoreFixtures(); this.user = DiscussionUtil.getUser(); this.threadData = { diff --git a/common/static/common/js/spec/discussion/view/discussion_thread_view_spec.js b/common/static/common/js/spec/discussion/view/discussion_thread_view_spec.js index 9b39017d54..3a35cfdbc5 100644 --- a/common/static/common/js/spec/discussion/view/discussion_thread_view_spec.js +++ b/common/static/common/js/spec/discussion/view/discussion_thread_view_spec.js @@ -129,7 +129,7 @@ model: thread, el: $('#fixture-element'), mode: mode, - course_settings: DiscussionSpecHelper.makeCourseSettings() + course_settings: DiscussionSpecHelper.createTestCourseSettings() }); renderWithTestResponses(view, 1); if (mode === 'inline') { @@ -185,7 +185,7 @@ model: this.thread, el: $('#fixture-element'), mode: 'tab', - course_settings: DiscussionSpecHelper.makeCourseSettings() + course_settings: DiscussionSpecHelper.createTestCourseSettings() }); }); describe('responses', function() { @@ -282,7 +282,7 @@ model: this.thread, el: $('#fixture-element'), mode: 'inline', - course_settings: DiscussionSpecHelper.makeCourseSettings() + course_settings: DiscussionSpecHelper.createTestCourseSettings() }); }); describe('render', function() { @@ -397,7 +397,7 @@ model: this.thread, el: $('#fixture-element'), mode: 'tab', - course_settings: DiscussionSpecHelper.makeCourseSettings() + course_settings: DiscussionSpecHelper.createTestCourseSettings() }); }); generateContent = function(idStart, idEnd) { @@ -465,7 +465,7 @@ model: this.thread, el: $('#fixture-element'), mode: 'tab', - course_settings: DiscussionSpecHelper.makeCourseSettings() + course_settings: DiscussionSpecHelper.createTestCourseSettings() }); }); it("doesn't show report option if can_report ability is disabled", function() { diff --git a/common/static/common/js/spec/discussion/view/response_comment_view_spec.js b/common/static/common/js/spec/discussion/view/response_comment_view_spec.js index 57d5fbfeea..35ba510892 100644 --- a/common/static/common/js/spec/discussion/view/response_comment_view_spec.js +++ b/common/static/common/js/spec/discussion/view/response_comment_view_spec.js @@ -5,7 +5,7 @@ describe('ResponseCommentView', function() { beforeEach(function() { - DiscussionSpecHelper.setUpGlobals(); + DiscussionSpecHelper.setUpGlobals({}); this.comment = new Comment({ id: '01234567', user_id: user.id, diff --git a/common/static/common/js/spec_helpers/discussion_spec_helper.js b/common/static/common/js/spec_helpers/discussion_spec_helper.js index c255106e3c..bae2354e70 100644 --- a/common/static/common/js/spec_helpers/discussion_spec_helper.js +++ b/common/static/common/js/spec_helpers/discussion_spec_helper.js @@ -1,15 +1,16 @@ -/* global DiscussionCourseSettings, DiscussionUtil, DiscussionUser */ +/* global Content, Discussion, DiscussionCourseSettings, DiscussionUtil, DiscussionUser */ (function() { 'use strict'; this.DiscussionSpecHelper = (function() { function DiscussionSpecHelper() { } - DiscussionSpecHelper.setUpGlobals = function() { - DiscussionUtil.loadRoles(DiscussionSpecHelper.getTestRoleInfo()); - window.$$course_id = 'edX/999/test'; - window.user = new DiscussionUser(DiscussionSpecHelper.getTestUserInfo()); - return DiscussionUtil.setUser(window.user); + DiscussionSpecHelper.setUpGlobals = function(opts) { + var options = opts || {}; + DiscussionUtil.loadRoles(options.roles || DiscussionSpecHelper.getTestRoleInfo()); + window.$$course_id = options.courseName || 'edX/999/test'; + window.user = new DiscussionUser(options.userInfo || DiscussionSpecHelper.getTestUserInfo()); + DiscussionUtil.setUser(window.user); }; DiscussionSpecHelper.getTestUserInfo = function() { @@ -50,7 +51,7 @@ return jasmine.createSpyObj('event', ['preventDefault', 'target']); }; - DiscussionSpecHelper.makeCourseSettings = function() { + DiscussionSpecHelper.createTestCourseSettings = function() { return new DiscussionCourseSettings({ category_map: { children: ['Test Topic', 'Other Topic'], @@ -69,12 +70,24 @@ }); }; + DiscussionSpecHelper.createTestDiscussion = function(options) { + var sortPreference = options.sort_preference, + threads = options.threads || [], + threadPages = options.thread_pages || 1, + contentInfo = options.content_info; + DiscussionSpecHelper.setUpGlobals(options); + if (contentInfo) { + Content.loadContentInfos(contentInfo); + } + return new Discussion(threads, {pages: threadPages, sort: sortPreference}); + }; + DiscussionSpecHelper.setUnderscoreFixtures = function() { var templateFixture, templateName, templateNames, templateNamesNoTrailingTemplate, i, j, len; templateNames = [ 'thread', 'thread-show', 'thread-edit', 'thread-response', 'thread-response-show', 'thread-response-edit', 'response-comment-show', 'response-comment-edit', 'thread-list-item', - 'discussion-home', 'search-alert', 'new-post', 'thread-type', 'new-post-menu-entry', 'new-post-alert', + 'search-alert', 'new-post', 'thread-type', 'new-post-menu-entry', 'new-post-alert', 'new-post-menu-category', 'topic', 'post-user-display', 'inline-discussion', 'pagination', 'profile-thread', 'customwmd-prompt', 'nav-loading' ]; diff --git a/common/test/acceptance/tests/discussion/test_discussion.py b/common/test/acceptance/tests/discussion/test_discussion.py index 897628eacf..4899c937ae 100644 --- a/common/test/acceptance/tests/discussion/test_discussion.py +++ b/common/test/acceptance/tests/discussion/test_discussion.py @@ -511,6 +511,7 @@ class DiscussionOpenClosedThreadTest(BaseDiscussionTestCase): page.a11y_audit.config.set_rules({ 'ignore': [ 'section', # TODO: AC-491 + 'aria-required-children', # TODO: AC-534 'color-contrast', # Commented out for now because they reproducibly fail on Jenkins but not locally ] }) @@ -520,6 +521,7 @@ class DiscussionOpenClosedThreadTest(BaseDiscussionTestCase): page.a11y_audit.config.set_rules({ 'ignore': [ 'section', # TODO: AC-491 + 'aria-required-children', # TODO: AC-534 'color-contrast', # Commented out for now because they reproducibly fail on Jenkins but not locally ] }) diff --git a/lms/djangoapps/discussion/static/discussion/js/discussion_board_factory.js b/lms/djangoapps/discussion/static/discussion/js/discussion_board_factory.js index bea9bd56dc..6c2c310343 100644 --- a/lms/djangoapps/discussion/static/discussion/js/discussion_board_factory.js +++ b/lms/djangoapps/discussion/static/discussion/js/discussion_board_factory.js @@ -5,38 +5,50 @@ [ 'jquery', 'backbone', + 'common/js/discussion/content', + 'common/js/discussion/discussion', + 'common/js/discussion/utils', + 'common/js/discussion/models/discussion_course_settings', + 'common/js/discussion/models/discussion_user', + 'common/js/discussion/views/new_post_view', 'discussion/js/discussion_router', - 'discussion/js/views/discussion_fake_breadcrumbs', - 'discussion/js/views/discussion_search_view', - 'common/js/discussion/views/new_post_view' + 'discussion/js/views/discussion_board_view' ], - function($, Backbone, DiscussionRouter, DiscussionFakeBreadcrumbs, DiscussionSearchView, NewPostView) { + function($, Backbone, Content, Discussion, DiscussionUtil, DiscussionCourseSettings, DiscussionUser, + NewPostView, DiscussionRouter, DiscussionBoardView) { return function(options) { var userInfo = options.user_info, sortPreference = options.sort_preference, threads = options.threads, threadPages = options.thread_pages, contentInfo = options.content_info, - user = new window.DiscussionUser(userInfo), + user = new DiscussionUser(userInfo), discussion, courseSettings, newPostView, + discussionBoardView, router, - breadcrumbs, - BreadcrumbsModel, - searchBox, routerEvents; - // TODO: Perhaps eliminate usage of global variables when possible - window.DiscussionUtil.loadRoles(options.roles); + // TODO: eliminate usage of global variables when possible + DiscussionUtil.loadRoles(options.roles); window.$$course_id = options.courseId; window.courseName = options.course_name; - window.DiscussionUtil.setUser(user); + DiscussionUtil.setUser(user); window.user = user; - window.Content.loadContentInfos(contentInfo); + Content.loadContentInfos(contentInfo); - discussion = new window.Discussion(threads, {pages: threadPages, sort: sortPreference}); - courseSettings = new window.DiscussionCourseSettings(options.course_settings); + // Create a discussion model + discussion = new Discussion(threads, {pages: threadPages, sort: sortPreference}); + courseSettings = new DiscussionCourseSettings(options.course_settings); + + // Create the discussion board view + discussionBoardView = new DiscussionBoardView({ + el: $('.discussion-board'), + discussion: discussion, + courseSettings: courseSettings + }); + discussionBoardView.render(); // Create the new post view newPostView = new NewPostView({ @@ -47,59 +59,27 @@ }); newPostView.render(); - // Set up the router to manage the page's history + // Set up a router to manage the page's history router = new DiscussionRouter({ courseId: options.courseId, discussion: discussion, courseSettings: courseSettings, + discussionBoardView: discussionBoardView, newPostView: newPostView }); router.start(); - - // Initialize and render search box - searchBox = new DiscussionSearchView({ - el: $('.forum-search'), - threadListView: router.nav - }).render(); - - // Initialize and render breadcrumbs - BreadcrumbsModel = Backbone.Model.extend({ - defaults: { - contents: [] - } - }); - - breadcrumbs = new DiscussionFakeBreadcrumbs({ - el: $('.has-breadcrumbs'), - model: new BreadcrumbsModel(), - events: { - 'click .all-topics': function(event) { - event.preventDefault(); - searchBox.clearSearch(); - this.model.set('contents', []); - router.navigate('', {trigger: true}); - router.nav.toggleBrowseMenu(event); - } - } - }).render(); - routerEvents = { // Add new breadcrumbs and clear search box when the user selects topics 'topic:selected': function(topic) { - breadcrumbs.model.set('contents', topic); + router.discussionBoardView.breadcrumbs.model.set('contents', topic); }, // Clear search box when a thread is selected 'thread:selected': function() { - searchBox.clearSearch(); - }, - // Add 'Search Results' to breadcrumbs when user searches - 'search:initiated': function() { - breadcrumbs.model.set('contents', ['Search Results']); + router.discussionBoardView.searchView.clearSearch(); } }; - Object.keys(routerEvents).forEach(function(key) { - router.nav.on(key, routerEvents[key]); + router.discussionBoardView.on(key, routerEvents[key]); }); }; }); diff --git a/lms/djangoapps/discussion/static/discussion/js/discussion_profile_page_factory.js b/lms/djangoapps/discussion/static/discussion/js/discussion_profile_page_factory.js index 0b59b15a91..537418a102 100644 --- a/lms/djangoapps/discussion/static/discussion/js/discussion_profile_page_factory.js +++ b/lms/djangoapps/discussion/static/discussion/js/discussion_profile_page_factory.js @@ -1,8 +1,14 @@ (function(define) { 'use strict'; - define(['jquery', 'discussion/js/views/discussion_user_profile_view'], - function($, DiscussionUserProfileView) { + define( + [ + 'jquery', + 'common/js/discussion/utils', + 'common/js/discussion/models/discussion_user', + 'discussion/js/views/discussion_user_profile_view' + ], + function($, DiscussionUtil, DiscussionUser, DiscussionUserProfileView) { return function(options) { var $element = options.$el, threads = options.threads, @@ -10,13 +16,16 @@ page = options.page, numPages = options.numPages; // Roles are not included in user profile page, but they are not used for anything - window.DiscussionUtil.loadRoles({ + DiscussionUtil.loadRoles({ Moderator: [], Administrator: [], 'Community TA': [] }); + + // TODO: remove global variable usage window.$$course_id = options.courseId; - window.user = new window.DiscussionUser(userInfo); + window.user = new DiscussionUser(userInfo); + new DiscussionUserProfileView({ // eslint-disable-line no-new el: $element, collection: threads, diff --git a/lms/djangoapps/discussion/static/discussion/js/discussion_router.js b/lms/djangoapps/discussion/static/discussion/js/discussion_router.js index 1280940c2a..83a4d19cd8 100644 --- a/lms/djangoapps/discussion/static/discussion/js/discussion_router.js +++ b/lms/djangoapps/discussion/static/discussion/js/discussion_router.js @@ -6,10 +6,10 @@ 'underscore', 'backbone', 'common/js/discussion/utils', - 'common/js/discussion/views/discussion_thread_list_view', + 'common/js/discussion/models/discussion_course_settings', 'common/js/discussion/views/discussion_thread_view' ], - function(_, Backbone, DiscussionUtil, DiscussionThreadListView, DiscussionThreadView) { + function(_, Backbone, DiscussionUtil, DiscussionCourseSettings, DiscussionThreadView) { var DiscussionRouter = Backbone.Router.extend({ routes: { '': 'allThreads', @@ -21,14 +21,9 @@ _.bindAll(this, 'allThreads', 'showThread'); this.courseId = options.courseId; this.discussion = options.discussion; - this.course_settings = options.courseSettings; + this.course_settings = new DiscussionCourseSettings(options.course_settings); + this.discussionBoardView = options.discussionBoardView; this.newPostView = options.newPostView; - this.nav = new DiscussionThreadListView({ - collection: this.discussion, - el: $('.forum-nav'), - courseSettings: this.course_settings - }); - this.nav.render(); }, start: function() { @@ -41,10 +36,18 @@ }); // Automatically navigate when the user selects threads - this.nav.on('thread:selected', _.bind(this.navigateToThread, this)); - this.nav.on('thread:removed', _.bind(this.navigateToAllThreads, this)); - this.nav.on('threads:rendered', _.bind(this.setActiveThread, this)); - this.nav.on('thread:created', _.bind(this.navigateToThread, this)); + this.discussionBoardView.discussionThreadListView.on( + 'thread:selected', _.bind(this.navigateToThread, this) + ); + this.discussionBoardView.discussionThreadListView.on( + 'thread:removed', _.bind(this.navigateToAllThreads, this) + ); + this.discussionBoardView.discussionThreadListView.on( + 'threads:rendered', _.bind(this.setActiveThread, this) + ); + this.discussionBoardView.discussionThreadListView.on( + 'thread:created', _.bind(this.navigateToThread, this) + ); Backbone.history.start({ pushState: true, @@ -57,15 +60,15 @@ }, allThreads: function() { - this.nav.updateSidebar(); - return this.nav.goHome(); + this.discussionBoardView.updateSidebar(); + return this.discussionBoardView.goHome(); }, setActiveThread: function() { if (this.thread) { - return this.nav.setActiveThread(this.thread.get('id')); + return this.discussionBoardView.discussionThreadListView.setActiveThread(this.thread.get('id')); } else { - return this.nav.goHome; + return this.discussionBoardView.goHome; } }, @@ -86,8 +89,8 @@ if (!($('.forum-content').is(':visible'))) { $('.forum-content').fadeIn(); } - if (this.newPostView.$el.is(':visible')) { - this.newPostView.$el.fadeOut(); + if ($('.new-post-article').is(':visible')) { + $('.new-post-article').fadeOut(); } this.main = new DiscussionThreadView({ el: $('.forum-content'), @@ -97,14 +100,13 @@ }); this.main.render(); this.main.on('thread:responses:rendered', function() { - return self.nav.updateSidebar(); + return self.discussionBoardView.updateSidebar(); }); return this.thread.on('thread:thread_type_updated', this.showMain); }, navigateToThread: function(threadId) { - var thread; - thread = this.discussion.get(threadId); + var thread = this.discussion.get(threadId); return this.navigate('' + (thread.get('commentable_id')) + '/threads/' + threadId, { trigger: true }); @@ -135,6 +137,7 @@ } }); } + }); return DiscussionRouter; diff --git a/lms/djangoapps/discussion/static/discussion/js/spec/discussion_board_factory_spec.js b/lms/djangoapps/discussion/static/discussion/js/spec/discussion_board_factory_spec.js index 53e49dbd70..cf571920a1 100644 --- a/lms/djangoapps/discussion/static/discussion/js/spec/discussion_board_factory_spec.js +++ b/lms/djangoapps/discussion/static/discussion/js/spec/discussion_board_factory_spec.js @@ -4,16 +4,34 @@ define( 'backbone', 'common/js/spec_helpers/page_helpers', 'common/js/spec_helpers/discussion_spec_helper', - 'discussion/js/discussion_board_factory' + 'discussion/js/discussion_board_factory', + 'discussion/js/views/discussion_board_view' ], - function($, Backbone, PageHelpers, DiscussionSpecHelper, DiscussionBoardFactory) { + function($, Backbone, PageHelpers, DiscussionSpecHelper, DiscussionBoardFactory, DiscussionBoardView) { 'use strict'; // TODO: re-enable when this doesn't interact badly with other history tests - xdescribe('Discussion Board Factory', function() { + describe('DiscussionBoardFactory', function() { + var createDiscussionBoardView = function() { + var discussionBoardView, + discussion = DiscussionSpecHelper.createTestDiscussion({}), + courseSettings = DiscussionSpecHelper.createTestCourseSettings(); + + setFixtures('
'); + DiscussionSpecHelper.setUnderscoreFixtures(); + + discussionBoardView = new DiscussionBoardView({ + el: $('.discussion-board'), + discussion: discussion, + courseSettings: courseSettings + }); + + return discussionBoardView; + }; + var initializeDiscussionBoardFactory = function() { DiscussionBoardFactory({ - el: $('.discussion-board'), + el: $('#discussion-container'), courseId: 'test_course_id', course_name: 'Test Course', user_info: DiscussionSpecHelper.getTestUserInfo(), @@ -33,14 +51,11 @@ define( }; beforeEach(function() { - PageHelpers.preventBackboneChangingUrl(); - // Install the fixtures setFixtures( - '
' + - '
' + - '
' + '
' ); + PageHelpers.preventBackboneChangingUrl(); DiscussionSpecHelper.setUnderscoreFixtures(); }); @@ -48,9 +63,11 @@ define( Backbone.history.stop(); }); - it('can render itself', function() { + xit('can render itself', function() { // this failed Search: navigates to search, and TeamsTab + var discussionView = createDiscussionBoardView(); + discussionView.render(); initializeDiscussionBoardFactory(); - expect($('.discussion-board').text()).toContain('All Discussions'); + expect(discussionView.$el.text()).toContain('Search all posts'); }); }); } diff --git a/lms/djangoapps/discussion/static/discussion/js/spec/discussion_board_view_spec.js b/lms/djangoapps/discussion/static/discussion/js/spec/discussion_board_view_spec.js new file mode 100644 index 0000000000..ccee1de7da --- /dev/null +++ b/lms/djangoapps/discussion/static/discussion/js/spec/discussion_board_view_spec.js @@ -0,0 +1,58 @@ +/* globals Discussion, DiscussionCourseSettings */ +(function(define) { + 'use strict'; + define( + [ + 'underscore', + 'jquery', + 'edx-ui-toolkit/js/utils/constants', + 'common/js/discussion/discussion', + 'common/js/spec_helpers/discussion_spec_helper', + 'discussion/js/views/discussion_board_view' + ], + function(_, $, constants, Discussion, DiscussionSpecHelper, DiscussionBoardView) { + describe('DiscussionBoardView', function() { + var createDiscussionBoardView; + createDiscussionBoardView = function() { + var discussionBoardView, + discussion = DiscussionSpecHelper.createTestDiscussion({}), + courseSettings = DiscussionSpecHelper.createTestCourseSettings(); + + setFixtures('
'); + DiscussionSpecHelper.setUnderscoreFixtures(); + + discussionBoardView = new DiscussionBoardView({ + el: $('.discussion-board'), + discussion: discussion, + courseSettings: courseSettings + }); + + return discussionBoardView; + }; + + describe('Search events', function() { + it('perform search when enter pressed inside search textfield', function() { + var discussionBoardView = createDiscussionBoardView(), + threadListView; + discussionBoardView.render(); + threadListView = discussionBoardView.discussionThreadListView; + spyOn(threadListView, 'performSearch'); + discussionBoardView.$('.search-input').trigger($.Event('keydown', { + which: constants.keyCodes.enter + })); + expect(threadListView.performSearch).toHaveBeenCalled(); + }); + + it('perform search when search icon is clicked', function() { + var discussionBoardView = createDiscussionBoardView(), + threadListView; + discussionBoardView.render(); + threadListView = discussionBoardView.discussionThreadListView; + spyOn(threadListView, 'performSearch'); + discussionBoardView.$el.find('.search-btn').click(); + expect(threadListView.performSearch).toHaveBeenCalled(); + }); + }); + }); + }); +}).call(this, define || RequireJS.define); diff --git a/lms/djangoapps/discussion/static/discussion/js/spec/views/discussion_search_view_spec.js b/lms/djangoapps/discussion/static/discussion/js/spec/views/discussion_search_view_spec.js deleted file mode 100644 index f7c05f4daf..0000000000 --- a/lms/djangoapps/discussion/static/discussion/js/spec/views/discussion_search_view_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -define([ - 'jquery', - 'edx-ui-toolkit/js/utils/constants', - 'discussion/js/views/discussion_search_view' -], - function($, constants, DiscussionSearchView) { - 'use strict'; - - describe('DiscussionSearchView', function() { - var view; - beforeEach(function() { - setFixtures('
'); - view = new DiscussionSearchView({ - el: $('.search-container'), - threadListView: { - performSearch: jasmine.createSpy() - } - }).render(); - }); - - describe('Search events', function() { - it('perform search when enter pressed inside search textfield', function() { - view.$el.find('.search-input').trigger($.Event('keydown', { - which: constants.keyCodes.enter - })); - expect(view.threadListView.performSearch).toHaveBeenCalled(); - }); - - it('perform search when search icon is clicked', function() { - view.$el.find('.search-btn').click(); - expect(view.threadListView.performSearch).toHaveBeenCalled(); - }); - }); - }); - } -); diff --git a/lms/djangoapps/discussion/static/discussion/js/views/discussion_board_view.js b/lms/djangoapps/discussion/static/discussion/js/views/discussion_board_view.js new file mode 100644 index 0000000000..1c092d2675 --- /dev/null +++ b/lms/djangoapps/discussion/static/discussion/js/views/discussion_board_view.js @@ -0,0 +1,336 @@ +/* globals Discussion */ +(function(define) { + 'use strict'; + + define([ + 'underscore', + 'backbone', + 'edx-ui-toolkit/js/utils/html-utils', + 'edx-ui-toolkit/js/utils/constants', + 'common/js/discussion/utils', + 'common/js/discussion/views/discussion_thread_list_view', + 'discussion/js/views/discussion_fake_breadcrumbs', + 'discussion/js/views/discussion_search_view', + 'text!discussion/templates/discussion-home.underscore' + ], + function(_, Backbone, HtmlUtils, Constants, DiscussionUtil, + DiscussionThreadListView, DiscussionFakeBreadcrumbs, DiscussionSearchView, discussionHomeTemplate) { + var DiscussionBoardView = Backbone.View.extend({ + events: { + 'click .forum-nav-browse-title': 'selectTopicHandler', + 'click .all-topics': 'toggleBrowseMenu', + 'keypress .forum-nav-browse-filter-input': function(event) { + return DiscussionUtil.ignoreEnterKey(event); + }, + 'keyup .forum-nav-browse-filter-input': 'filterTopics', + 'keydown .forum-nav-browse-filter-input': 'keyboardBinding', + 'click .forum-nav-browse-menu-wrapper': 'ignoreClick', + 'keydown .search-input': 'performSearch', + 'click .search-btn': 'performSearch', + 'topic:selected': 'clearSearch' + }, + + initialize: function(options) { + this.courseSettings = options.courseSettings; + this.sidebar_padding = 10; + this.current_search = ''; + this.mode = 'all'; + this.discussion = options.discussion; + this.filterInputReset(); + this.selectedTopic = $('.forum-nav-browse-menu-item:visible .forum-nav-browse-title.is-focused'); + this.listenTo(this.model, 'change', this.render); + }, + + render: function() { + this.discussionThreadListView = new DiscussionThreadListView({ + collection: this.discussion, + el: this.$('.discussion-thread-list-container'), + courseSettings: this.courseSettings + }).render(); + this.searchView = new DiscussionSearchView({ + el: this.$('.forum-search') + }).render(); + this.renderBreadcrumbs(); + $(window).bind('load scroll resize', this.updateSidebar); + this.showBrowseMenu(true); + return this; + }, + + renderBreadcrumbs: function() { + var BreadcrumbsModel = Backbone.Model.extend({ + defaults: { + contents: [] + } + }); + + this.breadcrumbs = new DiscussionFakeBreadcrumbs({ + el: $('.has-breadcrumbs'), + model: new BreadcrumbsModel(), + events: { + 'click .all-topics': function(event) { + event.preventDefault(); + } + } + }).render(); + }, + + isBrowseMenuVisible: function() { + return this.$('.forum-nav-browse-menu-wrapper').is(':visible'); + }, + + showBrowseMenu: function(initialLoad) { + if (!this.isBrowseMenuVisible()) { + this.$('.forum-nav-browse-menu-wrapper').show(); + this.$('.forum-nav-thread-list-wrapper').hide(); + if (!initialLoad) { + $('.forum-nav-browse-filter-input').focus(); + this.filterInputReset(); + } + this.updateSidebar(); + } + }, + + hideBrowseMenu: function() { + var selectedTopicList = this.$('.forum-nav-browse-title.is-focused'); + if (this.isBrowseMenuVisible()) { + selectedTopicList.removeClass('is-focused'); + this.$('.forum-nav-browse-menu-wrapper').hide(); + this.$('.forum-nav-thread-list-wrapper').show(); + if (this.selectedTopicId !== 'undefined') { + this.$('.forum-nav-browse-filter-input').attr('aria-activedescendant', this.selectedTopicId); + } + this.updateSidebar(); + } + }, + + toggleBrowseMenu: function(event) { + var inputText = this.$('.forum-nav-browse-filter-input').val(); + event.preventDefault(); + event.stopPropagation(); + if (this.isBrowseMenuVisible()) { + this.hideBrowseMenu(); + } else { + if (inputText !== '') { + this.filterTopics(inputText); + } + this.showBrowseMenu(); + } + this.breadcrumbs.model.set('contents', []); + this.clearSearch(); + }, + + performSearch: function(event) { + if (event.which === Constants.keyCodes.enter || event.type === 'click') { + event.preventDefault(); + this.hideBrowseMenu(); + this.breadcrumbs.model.set('contents', ['Search Results']); + this.discussionThreadListView.performSearch($('.search-input', this.$el)); + } + }, + + clearSearch: function() { + this.$('.search-input').val(''); + this.discussionThreadListView.clearSearchAlerts(); + }, + + updateSidebar: function() { + var amount, browseFilterHeight, discussionBottomOffset, discussionsBodyBottom, + discussionsBodyTop, headerHeight, refineBarHeight, scrollTop, sidebarHeight, topOffset, + windowHeight, $discussionBody, $sidebar; + scrollTop = $(window).scrollTop(); + windowHeight = $(window).height(); + $discussionBody = this.$('.discussion-column'); + discussionsBodyTop = $discussionBody[0] ? $discussionBody.offset().top : undefined; + discussionsBodyBottom = discussionsBodyTop + $discussionBody.outerHeight(); + $sidebar = this.$('.forum-nav'); + if (scrollTop > discussionsBodyTop - this.sidebar_padding) { + $sidebar.css('top', scrollTop - discussionsBodyTop + this.sidebar_padding); + } else { + $sidebar.css('top', '0'); + } + sidebarHeight = windowHeight - Math.max(discussionsBodyTop - scrollTop, this.sidebar_padding); + topOffset = scrollTop + windowHeight; + discussionBottomOffset = discussionsBodyBottom + this.sidebar_padding; + amount = Math.max(topOffset - discussionBottomOffset, 0); + sidebarHeight = sidebarHeight - this.sidebar_padding - amount; + sidebarHeight = Math.min(sidebarHeight + 1, $discussionBody.outerHeight()); + $sidebar.css('height', sidebarHeight); + headerHeight = this.$('.forum-nav-header').outerHeight(); + refineBarHeight = this.$('.forum-nav-refine-bar').outerHeight(); + browseFilterHeight = this.$('.forum-nav-browse-filter').outerHeight(); + this.$('.forum-nav-thread-list') + .css('height', (sidebarHeight - headerHeight - refineBarHeight - 2) + 'px'); + this.$('.forum-nav-browse-menu') + .css('height', (sidebarHeight - headerHeight - browseFilterHeight - 2) + 'px'); + }, + + goHome: function() { + var url = DiscussionUtil.urlFor('notifications_status', window.user.get('id')); + HtmlUtils.append(this.$('.forum-content').empty(), HtmlUtils.template(discussionHomeTemplate)({})); + this.$('.forum-nav-thread-list a').removeClass('is-active').find('.sr') + .remove(); + this.$('input.email-setting').bind('click', this.updateEmailNotifications); + DiscussionUtil.safeAjax({ + url: url, + type: 'GET', + success: function(response) { + $('input.email-setting').prop('checked', response.status); + } + }); + }, + + filterInputReset: function() { + this.filterEnabled = true; + this.selectedTopicIndex = -1; + this.selectedTopicId = null; + }, + + selectOption: function(element) { + var activeDescendantId, activeDescendantText; + if (this.selectedTopic.length > 0) { + this.selectedTopic.removeClass('is-focused'); + } + if (element) { + element.addClass('is-focused'); + activeDescendantId = element.parent().attr('id'); + activeDescendantText = element.text(); + this.selectedTopic = element; + this.selectedTopicId = activeDescendantId; + this.$('.forum-nav-browse-filter-input') + .attr('aria-activedescendant', activeDescendantId) + .val(activeDescendantText); + } + }, + + keyboardBinding: function(event) { + var key = event.which, + $inputText = $('.forum-nav-browse-filter-input'), + $filteredMenuItems = $('.forum-nav-browse-menu-item:visible'), + filteredMenuItemsLen = $filteredMenuItems.length, + $curOption = $filteredMenuItems.eq(0).find('.forum-nav-browse-title').eq(0), + $activeOption, $prev, $next; + + switch (key) { + case Constants.keyCodes.enter: + $activeOption = $filteredMenuItems.find('.forum-nav-browse-title.is-focused'); + if ($inputText.val() !== '') { + $activeOption.trigger('click'); + this.filterInputReset(); + } + break; + + case Constants.keyCodes.esc: + this.toggleBrowseMenu(event); + this.$('.forum-nav-browse-filter-input').val(''); + this.filterInputReset(); + $('.all-topics').trigger('click'); + break; + + case Constants.keyCodes.up: + if (this.selectedTopicIndex > 0) { + this.selectedTopicIndex -= 1; + if (this.isBrowseMenuVisible()) { + $prev = $('.forum-nav-browse-menu-item:visible') + .eq(this.selectedTopicIndex).find('.forum-nav-browse-title') + .eq(0); + this.filterEnabled = false; + $curOption.removeClass('is-focused'); + $prev.addClass('is-focused'); + } + this.selectOption($prev); + } + break; + + case Constants.keyCodes.down: + if (this.selectedTopicIndex < filteredMenuItemsLen - 1) { + this.selectedTopicIndex += 1; + if (this.isBrowseMenuVisible()) { + $next = $('.forum-nav-browse-menu-item:visible') + .eq(this.selectedTopicIndex).find('.forum-nav-browse-title') + .eq(0); + this.filterEnabled = false; + $curOption.removeClass('is-focused'); + $next.addClass('is-focused'); + } + this.selectOption($next); + } + break; + + default: + } + }, + + filterTopics: function() { + var $items, query, filteredItems, + self = this; + query = this.$('.forum-nav-browse-filter-input').val(); + $items = this.$('.forum-nav-browse-menu-item'); + if (query.length === 0) { + $items.find('.forum-nav-browse-title.is-focused').removeClass('is-focused'); + return $items.show(); + } else { + if (self.filterEnabled) { + $items.hide(); + filteredItems = $items.each(function(i, item) { + var path, pathText, + $item = $(item); + if (!$item.is(':visible')) { + pathText = self.getPathText($item).toLowerCase(); + if (query.split(' ').every(function(term) { + return pathText.search(term.toLowerCase()) !== -1; + })) { + path = $item.parents('.forum-nav-browse-menu-item').andSelf(); + path.add($item.find('.forum-nav-browse-menu-item')).show(); + } + } + }); + } + return filteredItems; + } + }, + + getPathText: function(item) { + var path, pathTitles; + path = item.parents('.forum-nav-browse-menu-item').andSelf(); + pathTitles = path.children('.forum-nav-browse-title').map(function(i, elem) { + return $(elem).text(); + }).get(); + return pathTitles.join(' / '); + }, + + selectTopicHandler: function(event) { + var $item = $(event.target).closest('.forum-nav-browse-menu-item'); + event.preventDefault(); + this.hideBrowseMenu(); + this.trigger('topic:selected', this.getBreadcrumbText($item)); + return this.discussionThreadListView.selectTopic($(event.target)); + }, + + getBreadcrumbText: function($item) { + var $parentSubMenus = $item.parents('.forum-nav-browse-submenu'), + crumbs = [], + subTopic = $('.forum-nav-browse-title', $item) + .first() + .text() + .trim(); + + $parentSubMenus.each(function(i, el) { + crumbs.push($(el).siblings('.forum-nav-browse-title') + .first() + .text() + .trim() + ); + }); + + if (subTopic !== 'All Discussions') { + crumbs.push(subTopic); + } + + return crumbs; + } + + }); + + return DiscussionBoardView; + }); +}).call(this, define || RequireJS.define); diff --git a/lms/djangoapps/discussion/static/discussion/js/views/discussion_search_view.js b/lms/djangoapps/discussion/static/discussion/js/views/discussion_search_view.js index 9d1dd00620..ebf122b402 100644 --- a/lms/djangoapps/discussion/static/discussion/js/views/discussion_search_view.js +++ b/lms/djangoapps/discussion/static/discussion/js/views/discussion_search_view.js @@ -15,33 +15,16 @@ * in order to clean up that file and make it possible to break its logic into files like this one. */ var searchView = Backbone.View.extend({ - events: { - 'keydown .search-input': 'performSearch', - 'click .search-btn': 'performSearch', - 'topic:selected': 'clearSearch' - }, initialize: function(options) { - _.extend(this, _.pick(options, 'threadListView')); + _.extend(this, _.pick(options, ['discussionBoardView'])); this.template = HtmlUtils.template(searchTemplate); - this.threadListView = options.threadListView; - this.listenTo(this.model, 'change', this.render); this.render(); }, render: function() { HtmlUtils.setHtml(this.$el, this.template()); return this; - }, - performSearch: function(event) { - if (event.which === constants.keyCodes.enter || event.type === 'click') { - event.preventDefault(); - this.threadListView.performSearch($('.search-input', this.$el)); - } - }, - clearSearch: function() { - this.$('.search-input').val(''); - this.threadListView.clearSearchAlerts(); } }); diff --git a/common/static/common/templates/discussion/discussion-home.underscore b/lms/djangoapps/discussion/static/discussion/templates/discussion-home.underscore similarity index 100% rename from common/static/common/templates/discussion/discussion-home.underscore rename to lms/djangoapps/discussion/static/discussion/templates/discussion-home.underscore diff --git a/lms/djangoapps/discussion/templates/discussion/discussion_board.html b/lms/djangoapps/discussion/templates/discussion/discussion_board.html index b2a4b6d0ac..7882739cf1 100644 --- a/lms/djangoapps/discussion/templates/discussion/discussion_board.html +++ b/lms/djangoapps/discussion/templates/discussion/discussion_board.html @@ -77,6 +77,8 @@ DiscussionBoardFactory({
diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js index 2a4b77f506..14d6d0c6a1 100644 --- a/lms/static/lms/js/spec/main.js +++ b/lms/static/lms/js/spec/main.js @@ -288,11 +288,11 @@ deps: ['jquery', 'underscore', 'slick.core', 'slick.grid'], init: function() { // Set global variables that the util code is expecting to be defined - require([ + require([ // eslint-disable-line global-require 'edx-ui-toolkit/js/utils/html-utils', 'edx-ui-toolkit/js/utils/string-utils' ], function(HtmlUtils, StringUtils) { - window.edx = edx || {}; + window.edx = window.edx || {}; window.edx.HtmlUtils = HtmlUtils; window.edx.StringUtils = StringUtils; }); @@ -383,7 +383,7 @@ deps: ['jquery', 'underscore', 'underscore.string', 'backbone', 'gettext'], init: function() { // Set global variables that the payment code is expecting to be defined - require([ + require([ // eslint-disable-line global-require 'underscore', 'underscore.string', 'edx-ui-toolkit/js/utils/html-utils', @@ -391,7 +391,7 @@ ], function(_, str, HtmlUtils, StringUtils) { window._ = _; window._.str = str; - window.edx = edx || {}; + window.edx = window.edx || {}; window.edx.HtmlUtils = HtmlUtils; window.edx.StringUtils = StringUtils; }); @@ -527,10 +527,21 @@ exports: 'DiscussionUtil', init: function() { // Set global variables that the discussion code is expecting to be defined - require(['backbone', 'URI'], function(Backbone, URI) { - window.Backbone = Backbone; - window.URI = URI; - }); + require( // eslint-disable-line global-require + [ + 'backbone', + 'URI', + 'edx-ui-toolkit/js/utils/html-utils', + 'edx-ui-toolkit/js/utils/string-utils' + ], + function(Backbone, URI, HtmlUtils, StringUtils) { + window.Backbone = Backbone; + window.URI = URI; + window.edx = window.edx || {}; + window.edx.HtmlUtils = HtmlUtils; + window.edx.StringUtils = StringUtils; + } + ); } }, 'common/js/discussion/content': { @@ -542,11 +553,11 @@ 'common/js/discussion/discussion': { deps: [ 'common/js/discussion/utils', - 'xmodule_js/common_static/common/js/discussion/content' + 'common/js/discussion/content' ], exports: 'Discussion' }, - 'common/js/discussion/discussion_course_settings': { + 'common/js/discussion/models/discussion_course_settings': { deps: [ 'common/js/discussion/utils' ], @@ -664,7 +675,7 @@ var testFiles = [ 'discussion/js/spec/discussion_board_factory_spec.js', 'discussion/js/spec/discussion_profile_page_factory_spec.js', - 'discussion/js/spec/views/discussion_search_view_spec.js', + 'discussion/js/spec/discussion_board_view_spec.js', 'discussion/js/spec/views/discussion_user_profile_view_spec.js', 'lms/js/spec/preview/preview_factory_spec.js', 'js/spec/api_admin/catalog_preview_spec.js', diff --git a/lms/templates/discussion/_js_body_dependencies.html b/lms/templates/discussion/_js_body_dependencies.html index fda375a109..d4be582f9b 100644 --- a/lms/templates/discussion/_js_body_dependencies.html +++ b/lms/templates/discussion/_js_body_dependencies.html @@ -16,11 +16,14 @@ from openedx.core.djangolib.js_utils import js_escaped_string <% discussion_classes = [ ['Discussion', 'common/js/discussion/discussion'], + ['Content', 'common/js/discussion/content'], ['DiscussionModuleView', 'common/js/discussion/discussion_module_view'], ['DiscussionThreadView', 'common/js/discussion/views/discussion_thread_view'], ['DiscussionThreadListView', 'common/js/discussion/views/discussion_thread_list_view'], ['DiscussionThreadProfileView', 'common/js/discussion/views/discussion_thread_profile_view'], ['DiscussionUtil', 'common/js/discussion/utils'], + ['DiscussionCourseSettings', 'common/js/discussion/models/discussion_course_settings'], + ['DiscussionUser', 'common/js/discussion/models/discussion_user'], ['NewPostView', 'common/js/discussion/views/new_post_view'], ] %> diff --git a/lms/templates/discussion/_thread_list_template.html b/lms/templates/discussion/_thread_list_template.html index d0d73f7ce4..20a2d64170 100644 --- a/lms/templates/discussion/_thread_list_template.html +++ b/lms/templates/discussion/_thread_list_template.html @@ -1,8 +1,7 @@ <%page expression_filter="h"/> <%! from django.utils.translation import ugettext as _ %>