Cohort discussion topics via UI in instructor dashboard.
TNL-1256
This commit is contained in:
committed by
Usman Khalid
parent
e07f45b5dd
commit
e6c75529c0
14
lms/static/js/groups/models/cohort_discussions.js
Normal file
14
lms/static/js/groups/models/cohort_discussions.js
Normal file
@@ -0,0 +1,14 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function(Backbone) {
|
||||
'use strict';
|
||||
|
||||
edx.groups = edx.groups || {};
|
||||
|
||||
edx.groups.DiscussionTopicsSettingsModel = Backbone.Model.extend({
|
||||
defaults: {
|
||||
course_wide_discussions: {},
|
||||
inline_discussions: {}
|
||||
}
|
||||
});
|
||||
}).call(this, Backbone);
|
||||
@@ -9,7 +9,8 @@ var edx = edx || {};
|
||||
idAttribute: 'id',
|
||||
defaults: {
|
||||
is_cohorted: false,
|
||||
cohorted_discussions: [],
|
||||
cohorted_inline_discussions: [],
|
||||
cohorted_course_wide_discussions:[],
|
||||
always_cohort_inline_discussions: true
|
||||
}
|
||||
});
|
||||
|
||||
99
lms/static/js/groups/views/cohort_discussions.js
Normal file
99
lms/static/js/groups/views/cohort_discussions.js
Normal file
@@ -0,0 +1,99 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function ($, _, Backbone, gettext, interpolate_text, NotificationModel, NotificationView) {
|
||||
'use strict';
|
||||
|
||||
edx.groups = edx.groups || {};
|
||||
|
||||
edx.groups.CohortDiscussionConfigurationView = Backbone.View.extend({
|
||||
|
||||
/**
|
||||
* Add/Remove the disabled attribute on given element.
|
||||
* @param {object} $element - The element to disable/enable.
|
||||
* @param {bool} disable - The flag to add/remove 'disabled' attribute.
|
||||
*/
|
||||
setDisabled: function($element, disable) {
|
||||
$element.prop('disabled', disable ? 'disabled' : false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the cohorted discussions list.
|
||||
* @param {string} selector - To select the discussion elements whose ids to return.
|
||||
* @returns {Array} - Cohorted discussions.
|
||||
*/
|
||||
getCohortedDiscussions: function(selector) {
|
||||
var self=this,
|
||||
cohortedDiscussions = [];
|
||||
|
||||
_.each(self.$(selector), function (topic) {
|
||||
cohortedDiscussions.push($(topic).data('id'))
|
||||
});
|
||||
return cohortedDiscussions;
|
||||
},
|
||||
|
||||
/**
|
||||
* Save the cohortSettings' changed attributes to the server via PATCH method.
|
||||
* It shows the error message(s) if any.
|
||||
* @param {object} $element - Messages would be shown before this element.
|
||||
* @param {object} fieldData - Data to update on the server.
|
||||
*/
|
||||
saveForm: function ($element, fieldData) {
|
||||
var self = this,
|
||||
cohortSettingsModel = this.cohortSettings,
|
||||
saveOperation = $.Deferred(),
|
||||
showErrorMessage;
|
||||
|
||||
showErrorMessage = function (message, $element) {
|
||||
self.showMessage(message, $element, 'error');
|
||||
};
|
||||
this.removeNotification();
|
||||
|
||||
cohortSettingsModel.save(
|
||||
fieldData, {patch: true, wait: true}
|
||||
).done(function () {
|
||||
saveOperation.resolve();
|
||||
}).fail(function (result) {
|
||||
var errorMessage = null;
|
||||
try {
|
||||
var jsonResponse = JSON.parse(result.responseText);
|
||||
errorMessage = jsonResponse.error;
|
||||
} catch (e) {
|
||||
// Ignore the exception and show the default error message instead.
|
||||
}
|
||||
if (!errorMessage) {
|
||||
errorMessage = gettext("We've encountered an error. Refresh your browser and then try again.");
|
||||
}
|
||||
showErrorMessage(errorMessage, $element);
|
||||
saveOperation.reject();
|
||||
});
|
||||
return saveOperation.promise();
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the notification messages before given element using the NotificationModel.
|
||||
* @param {string} message - Text message to show.
|
||||
* @param {object} $element - Message would be shown before this element.
|
||||
* @param {string} type - Type of message to show e.g. confirmation or error.
|
||||
*/
|
||||
showMessage: function (message, $element, type) {
|
||||
var model = new NotificationModel({type: type || 'confirmation', title: message});
|
||||
this.removeNotification();
|
||||
this.notification = new NotificationView({
|
||||
model: model
|
||||
});
|
||||
$element.before(this.notification.$el);
|
||||
this.notification.render();
|
||||
},
|
||||
|
||||
/**
|
||||
*Removes the notification messages.
|
||||
*/
|
||||
removeNotification: function () {
|
||||
if (this.notification) {
|
||||
this.notification.remove();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}).call(this, $, _, Backbone, gettext, interpolate_text, NotificationModel, NotificationView
|
||||
);
|
||||
83
lms/static/js/groups/views/cohort_discussions_course_wide.js
Normal file
83
lms/static/js/groups/views/cohort_discussions_course_wide.js
Normal file
@@ -0,0 +1,83 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function ($, _, Backbone, gettext, interpolate_text, CohortDiscussionConfigurationView) {
|
||||
'use strict';
|
||||
|
||||
edx.groups = edx.groups || {};
|
||||
|
||||
edx.groups.CourseWideDiscussionsView = CohortDiscussionConfigurationView.extend({
|
||||
events: {
|
||||
'change .check-discussion-subcategory-course-wide': 'discussionCategoryStateChanged',
|
||||
'click .cohort-course-wide-discussions-form .action-save': 'saveCourseWideDiscussionsForm'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.template = _.template($('#cohort-discussions-course-wide-tpl').text());
|
||||
this.cohortSettings = options.cohortSettings;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$('.cohort-course-wide-discussions-nav').html(this.template({
|
||||
courseWideTopics: this.getCourseWideDiscussionsHtml(
|
||||
this.model.get('course_wide_discussions')
|
||||
)
|
||||
}));
|
||||
this.setDisabled(this.$('.cohort-course-wide-discussions-form .action-save'), true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the html list for course-wide discussion topics.
|
||||
* @param {object} courseWideDiscussions - course-wide discussions object from server.
|
||||
* @returns {Array} - HTML list for course-wide discussion topics.
|
||||
*/
|
||||
getCourseWideDiscussionsHtml: function (courseWideDiscussions) {
|
||||
var subCategoryTemplate = _.template($('#cohort-discussions-subcategory-tpl').html()),
|
||||
entries = courseWideDiscussions.entries,
|
||||
children = courseWideDiscussions.children;
|
||||
|
||||
return _.map(children, function (name) {
|
||||
var entry = entries[name];
|
||||
return subCategoryTemplate({
|
||||
name: name,
|
||||
id: entry.id,
|
||||
is_cohorted: entry.is_cohorted,
|
||||
type: 'course-wide'
|
||||
});
|
||||
}).join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables the save button for course-wide discussions.
|
||||
*/
|
||||
discussionCategoryStateChanged: function(event) {
|
||||
event.preventDefault();
|
||||
this.setDisabled(this.$('.cohort-course-wide-discussions-form .action-save'), false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends the cohorted_course_wide_discussions to the server and renders the view.
|
||||
*/
|
||||
saveCourseWideDiscussionsForm: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var self = this,
|
||||
courseWideCohortedDiscussions = self.getCohortedDiscussions(
|
||||
'.check-discussion-subcategory-course-wide:checked'
|
||||
),
|
||||
fieldData = { cohorted_course_wide_discussions: courseWideCohortedDiscussions };
|
||||
|
||||
self.saveForm(self.$('.course-wide-discussion-topics'),fieldData)
|
||||
.done(function () {
|
||||
self.model.fetch()
|
||||
.done(function () {
|
||||
self.render();
|
||||
self.showMessage(gettext('Your changes have been saved.'), self.$('.course-wide-discussion-topics'));
|
||||
}).fail(function() {
|
||||
var errorMessage = gettext("We've encountered an error. Refresh your browser and then try again.");
|
||||
self.showMessage(errorMessage, self.$('.course-wide-discussion-topics'), 'error')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}).call(this, $, _, Backbone, gettext, interpolate_text, edx.groups.CohortDiscussionConfigurationView);
|
||||
145
lms/static/js/groups/views/cohort_discussions_inline.js
Normal file
145
lms/static/js/groups/views/cohort_discussions_inline.js
Normal file
@@ -0,0 +1,145 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function ($, _, Backbone, gettext, interpolate_text, CohortDiscussionConfigurationView) {
|
||||
'use strict';
|
||||
|
||||
edx.groups = edx.groups || {};
|
||||
|
||||
edx.groups.InlineDiscussionsView = CohortDiscussionConfigurationView.extend({
|
||||
events: {
|
||||
'change .check-discussion-category': 'setSaveButton',
|
||||
'change .check-discussion-subcategory-inline': 'setSaveButton',
|
||||
'click .cohort-inline-discussions-form .action-save': 'saveInlineDiscussionsForm',
|
||||
'change .check-all-inline-discussions': 'setAllInlineDiscussions',
|
||||
'change .check-cohort-inline-discussions': 'setSomeInlineDiscussions'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.template = _.template($('#cohort-discussions-inline-tpl').text());
|
||||
this.cohortSettings = options.cohortSettings;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var alwaysCohortInlineDiscussions = this.cohortSettings.get('always_cohort_inline_discussions');
|
||||
|
||||
this.$('.cohort-inline-discussions-nav').html(this.template({
|
||||
inlineDiscussionTopics: this.getInlineDiscussionsHtml(this.model.get('inline_discussions')),
|
||||
alwaysCohortInlineDiscussions:alwaysCohortInlineDiscussions
|
||||
}));
|
||||
|
||||
// Provides the semantics for a nested list of tri-state checkboxes.
|
||||
// When attached to a jQuery element it listens for change events to
|
||||
// input[type=checkbox] elements, and updates the checked and indeterminate
|
||||
// based on the checked values of any checkboxes in child elements of the DOM.
|
||||
this.$('ul.inline-topics').qubit();
|
||||
|
||||
this.setElementsEnabled(alwaysCohortInlineDiscussions, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate html list for inline discussion topics.
|
||||
* @params {object} inlineDiscussions - inline discussions object from server.
|
||||
* @returns {Array} - HTML for inline discussion topics.
|
||||
*/
|
||||
getInlineDiscussionsHtml: function (inlineDiscussions) {
|
||||
var categoryTemplate = _.template($('#cohort-discussions-category-tpl').html()),
|
||||
entryTemplate = _.template($('#cohort-discussions-subcategory-tpl').html()),
|
||||
isCategoryCohorted = false,
|
||||
children = inlineDiscussions.children,
|
||||
entries = inlineDiscussions.entries,
|
||||
subcategories = inlineDiscussions.subcategories;
|
||||
|
||||
return _.map(children, function (name) {
|
||||
var html = '', entry;
|
||||
if (entries && _.has(entries, name)) {
|
||||
entry = entries[name];
|
||||
html = entryTemplate({
|
||||
name: name,
|
||||
id: entry.id,
|
||||
is_cohorted: entry.is_cohorted,
|
||||
type: 'inline'
|
||||
});
|
||||
} else { // subcategory
|
||||
html = categoryTemplate({
|
||||
name: name,
|
||||
entries: this.getInlineDiscussionsHtml(subcategories[name]),
|
||||
isCategoryCohorted: isCategoryCohorted
|
||||
});
|
||||
}
|
||||
return html;
|
||||
}, this).join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable/Disable the inline discussion elements.
|
||||
*
|
||||
* Disables the category and sub-category checkboxes.
|
||||
* Enables the save button.
|
||||
*/
|
||||
setAllInlineDiscussions: function(event) {
|
||||
event.preventDefault();
|
||||
this.setElementsEnabled(($(event.currentTarget).prop('checked')), false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables the inline discussion elements.
|
||||
*
|
||||
* Enables the category and sub-category checkboxes.
|
||||
* Enables the save button.
|
||||
*/
|
||||
setSomeInlineDiscussions: function(event) {
|
||||
event.preventDefault();
|
||||
this.setElementsEnabled(!($(event.currentTarget).prop('checked')), false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable/Disable the inline discussion elements.
|
||||
*
|
||||
* Enable/Disable the category and sub-category checkboxes.
|
||||
* Enable/Disable the save button.
|
||||
* @param {bool} enable_checkboxes - The flag to enable/disable the checkboxes.
|
||||
* @param {bool} enable_save_button - The flag to enable/disable the save button.
|
||||
*/
|
||||
setElementsEnabled: function(enable_checkboxes, enable_save_button) {
|
||||
this.setDisabled(this.$('.check-discussion-category'), enable_checkboxes);
|
||||
this.setDisabled(this.$('.check-discussion-subcategory-inline'), enable_checkboxes);
|
||||
this.setDisabled(this.$('.cohort-inline-discussions-form .action-save'), enable_save_button);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables the save button for inline discussions.
|
||||
*/
|
||||
setSaveButton: function(event) {
|
||||
this.setDisabled(this.$('.cohort-inline-discussions-form .action-save'), false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends the cohorted_inline_discussions to the server and renders the view.
|
||||
*/
|
||||
saveInlineDiscussionsForm: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var self = this,
|
||||
cohortedInlineDiscussions = self.getCohortedDiscussions(
|
||||
'.check-discussion-subcategory-inline:checked'
|
||||
),
|
||||
fieldData= {
|
||||
cohorted_inline_discussions: cohortedInlineDiscussions,
|
||||
always_cohort_inline_discussions: self.$('.check-all-inline-discussions').prop('checked')
|
||||
};
|
||||
|
||||
self.saveForm(self.$('.inline-discussion-topics'), fieldData)
|
||||
.done(function () {
|
||||
self.model.fetch()
|
||||
.done(function () {
|
||||
self.render();
|
||||
self.showMessage(gettext('Your changes have been saved.'), self.$('.inline-discussion-topics'));
|
||||
}).fail(function() {
|
||||
var errorMessage = gettext("We've encountered an error. Refresh your browser and then try again.");
|
||||
self.showMessage(errorMessage, self.$('.inline-discussion-topics'), 'error')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}).call(this, $, _, Backbone, gettext, interpolate_text, edx.groups.CohortDiscussionConfigurationView);
|
||||
@@ -44,8 +44,7 @@ var edx = edx || {};
|
||||
|
||||
renderGroupHeader: function() {
|
||||
this.$('.cohort-management-group-header').html(this.groupHeaderTemplate({
|
||||
cohort: this.model,
|
||||
studioAdvancedSettingsUrl: this.context.studioAdvancedSettingsUrl
|
||||
cohort: this.model
|
||||
}));
|
||||
},
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, _, Backbone, gettext, interpolate_text, CohortModel, CohortEditorView, CohortFormView,
|
||||
CourseCohortSettingsNotificationView, NotificationModel, NotificationView, FileUploaderView) {
|
||||
CourseCohortSettingsNotificationView, NotificationModel, NotificationView, FileUploaderView,
|
||||
InlineDiscussionsView, CourseWideDiscussionsView) {
|
||||
'use strict';
|
||||
|
||||
var hiddenClass = 'is-hidden',
|
||||
@@ -17,7 +18,8 @@ var edx = edx || {};
|
||||
'click .cohort-management-add-form .action-save': 'saveAddCohortForm',
|
||||
'click .cohort-management-add-form .action-cancel': 'cancelAddCohortForm',
|
||||
'click .link-cross-reference': 'showSection',
|
||||
'click .toggle-cohort-management-secondary': 'showCsvUpload'
|
||||
'click .toggle-cohort-management-secondary': 'showCsvUpload',
|
||||
'click .toggle-cohort-management-discussions': 'showDiscussionTopics'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
@@ -120,7 +122,7 @@ var edx = edx || {};
|
||||
fieldData = {is_cohorted: this.getCohortsEnabled()};
|
||||
cohortSettings = this.cohortSettings;
|
||||
cohortSettings.save(
|
||||
fieldData, {wait: true}
|
||||
fieldData, {patch: true, wait: true}
|
||||
).done(function() {
|
||||
self.render();
|
||||
self.renderCourseCohortSettingsNotificationView();
|
||||
@@ -277,6 +279,27 @@ var edx = edx || {};
|
||||
this.$('#file-upload-form-file').focus();
|
||||
}
|
||||
},
|
||||
showDiscussionTopics: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
$(event.currentTarget).addClass(hiddenClass);
|
||||
var cohortDiscussionsElement = this.$('.cohort-discussions-nav').removeClass(hiddenClass);
|
||||
|
||||
if (!this.CourseWideDiscussionsView) {
|
||||
this.CourseWideDiscussionsView = new CourseWideDiscussionsView({
|
||||
el: cohortDiscussionsElement,
|
||||
model: this.context.discussionTopicsSettingsModel,
|
||||
cohortSettings: this.cohortSettings
|
||||
}).render();
|
||||
}
|
||||
if(!this.InlineDiscussionsView) {
|
||||
this.InlineDiscussionsView = new InlineDiscussionsView({
|
||||
el: cohortDiscussionsElement,
|
||||
model: this.context.discussionTopicsSettingsModel,
|
||||
cohortSettings: this.cohortSettings
|
||||
}).render();
|
||||
}
|
||||
},
|
||||
|
||||
getSectionCss: function (section) {
|
||||
return ".instructor-nav .nav-item a[data-section='" + section + "']";
|
||||
@@ -284,4 +307,4 @@ var edx = edx || {};
|
||||
});
|
||||
}).call(this, $, _, Backbone, gettext, interpolate_text, edx.groups.CohortModel, edx.groups.CohortEditorView,
|
||||
edx.groups.CohortFormView, edx.groups.CourseCohortSettingsNotificationView, NotificationModel, NotificationView,
|
||||
FileUploaderView);
|
||||
FileUploaderView, edx.groups.InlineDiscussionsView, edx.groups.CourseWideDiscussionsView);
|
||||
|
||||
@@ -1,33 +1,39 @@
|
||||
;(function (define, undefined) {
|
||||
'use strict';
|
||||
define(['jquery', 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/course_cohort_settings'],
|
||||
define(['jquery', 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/course_cohort_settings',
|
||||
'js/groups/models/cohort_discussions'],
|
||||
function($) {
|
||||
|
||||
return function(contentGroups, studioGroupConfigurationsUrl) {
|
||||
|
||||
var cohorts = new edx.groups.CohortCollection(),
|
||||
courseCohortSettings = new edx.groups.CourseCohortSettingsModel();
|
||||
courseCohortSettings = new edx.groups.CourseCohortSettingsModel(),
|
||||
discussionTopicsSettings = new edx.groups.DiscussionTopicsSettingsModel();
|
||||
|
||||
var cohortManagementElement = $('.cohort-management');
|
||||
|
||||
cohorts.url = cohortManagementElement.data('cohorts_url');
|
||||
courseCohortSettings.url = cohortManagementElement.data('course_cohort_settings_url');
|
||||
|
||||
discussionTopicsSettings.url = cohortManagementElement.data('discussion-topics-url');
|
||||
|
||||
var cohortsView = new edx.groups.CohortsView({
|
||||
el: cohortManagementElement,
|
||||
model: cohorts,
|
||||
contentGroups: contentGroups,
|
||||
cohortSettings: courseCohortSettings,
|
||||
context: {
|
||||
discussionTopicsSettingsModel: discussionTopicsSettings,
|
||||
uploadCohortsCsvUrl: cohortManagementElement.data('upload_cohorts_csv_url'),
|
||||
studioAdvancedSettingsUrl: cohortManagementElement.data('advanced-settings-url'),
|
||||
studioGroupConfigurationsUrl: studioGroupConfigurationsUrl
|
||||
}
|
||||
});
|
||||
|
||||
cohorts.fetch().done(function() {
|
||||
courseCohortSettings.fetch().done(function() {
|
||||
cohortsView.render();
|
||||
})
|
||||
discussionTopicsSettings.fetch().done(function() {
|
||||
cohortsView.render();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -1,10 +1,13 @@
|
||||
define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpers/template_helpers',
|
||||
'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/content_group',
|
||||
'js/groups/models/course_cohort_settings', 'js/utils/animation',
|
||||
'js/groups/views/course_cohort_settings_notification'
|
||||
'js/groups/models/course_cohort_settings', 'js/utils/animation', 'js/vendor/jquery.qubit',
|
||||
'js/groups/views/course_cohort_settings_notification', 'js/groups/models/cohort_discussions',
|
||||
'js/groups/views/cohort_discussions', 'js/groups/views/cohort_discussions_course_wide',
|
||||
'js/groups/views/cohort_discussions_inline'
|
||||
],
|
||||
function (Backbone, $, AjaxHelpers, TemplateHelpers, CohortsView, CohortCollection, ContentGroupModel,
|
||||
CourseCohortSettingsModel, AnimationUtil, CourseCohortSettingsNotificationView) {
|
||||
CourseCohortSettingsModel, AnimationUtil, Qubit, CourseCohortSettingsNotificationView, DiscussionTopicsSettingsModel,
|
||||
CohortDiscussionsView, CohortCourseWideDiscussionsView, CohortInlineDiscussionsView) {
|
||||
'use strict';
|
||||
|
||||
describe("Cohorts View", function () {
|
||||
@@ -14,7 +17,16 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
verifyDetailedMessage, verifyHeader, expectCohortAddRequest, getAddModal, selectContentGroup,
|
||||
clearContentGroup, saveFormAndExpectErrors, createMockCohortSettings, MOCK_COHORTED_USER_PARTITION_ID,
|
||||
MOCK_UPLOAD_COHORTS_CSV_URL, MOCK_STUDIO_ADVANCED_SETTINGS_URL, MOCK_STUDIO_GROUP_CONFIGURATIONS_URL,
|
||||
MOCK_MANUAL_ASSIGNMENT, MOCK_RANDOM_ASSIGNMENT;
|
||||
MOCK_MANUAL_ASSIGNMENT, MOCK_RANDOM_ASSIGNMENT, createMockCohortDiscussionsJson,
|
||||
createMockCohortDiscussions, showAndAssertDiscussionTopics;
|
||||
|
||||
// Selectors
|
||||
var discussionsToggle ='.toggle-cohort-management-discussions',
|
||||
inlineDiscussionsFormCss = '.cohort-inline-discussions-form',
|
||||
courseWideDiscussionsFormCss = '.cohort-course-wide-discussions-form',
|
||||
courseWideDiscussionsSaveButtonCss = '.cohort-course-wide-discussions-form .action-save',
|
||||
inlineDiscussionsSaveButtonCss = '.cohort-inline-discussions-form .action-save',
|
||||
inlineDiscussionsForm, courseWideDiscussionsForm;
|
||||
|
||||
MOCK_MANUAL_ASSIGNMENT = 'manual';
|
||||
MOCK_RANDOM_ASSIGNMENT = 'random';
|
||||
@@ -54,23 +66,71 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
];
|
||||
};
|
||||
|
||||
createMockCohortSettingsJson = function (isCohorted, cohortedDiscussions, alwaysCohortInlineDiscussions) {
|
||||
createMockCohortSettingsJson = function (isCohorted, cohortedInlineDiscussions, cohortedCourseWideDiscussions, alwaysCohortInlineDiscussions) {
|
||||
return {
|
||||
id: 0,
|
||||
is_cohorted: isCohorted || false,
|
||||
cohorted_discussions: cohortedDiscussions || [],
|
||||
cohorted_inline_discussions: cohortedInlineDiscussions || [],
|
||||
cohorted_course_wide_discussions: cohortedCourseWideDiscussions || [],
|
||||
always_cohort_inline_discussions: alwaysCohortInlineDiscussions || true
|
||||
};
|
||||
};
|
||||
|
||||
createMockCohortSettings = function (isCohorted, cohortedDiscussions, alwaysCohortInlineDiscussions) {
|
||||
createMockCohortSettings = function (isCohorted, cohortedInlineDiscussions, cohortedCourseWideDiscussions, alwaysCohortInlineDiscussions) {
|
||||
return new CourseCohortSettingsModel(
|
||||
createMockCohortSettingsJson(isCohorted, cohortedDiscussions, alwaysCohortInlineDiscussions)
|
||||
createMockCohortSettingsJson(isCohorted, cohortedInlineDiscussions, cohortedCourseWideDiscussions, alwaysCohortInlineDiscussions)
|
||||
);
|
||||
};
|
||||
|
||||
createMockCohortDiscussionsJson = function (allCohorted) {
|
||||
return {
|
||||
course_wide_discussions: {
|
||||
children: ['Topic_C_1', 'Topic_C_2'],
|
||||
entries: {
|
||||
Topic_C_1: {
|
||||
sort_key: null,
|
||||
is_cohorted: true,
|
||||
id: 'Topic_C_1'
|
||||
},
|
||||
Topic_C_2: {
|
||||
sort_key: null,
|
||||
is_cohorted: false,
|
||||
id: 'Topic_C_2'
|
||||
}
|
||||
}
|
||||
},
|
||||
inline_discussions: {
|
||||
subcategories: {
|
||||
Topic_I_1: {
|
||||
subcategories: {},
|
||||
children: ['Inline_Discussion_1', 'Inline_Discussion_2'],
|
||||
entries: {
|
||||
Inline_Discussion_1: {
|
||||
sort_key: null,
|
||||
is_cohorted: true,
|
||||
id: 'Inline_Discussion_1'
|
||||
},
|
||||
Inline_Discussion_2: {
|
||||
sort_key: null,
|
||||
is_cohorted: allCohorted || false,
|
||||
id: 'Inline_Discussion_2'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
children: ['Topic_I_1']
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
createMockCohortDiscussions = function (allCohorted) {
|
||||
return new DiscussionTopicsSettingsModel(
|
||||
createMockCohortDiscussionsJson(allCohorted)
|
||||
);
|
||||
};
|
||||
|
||||
createCohortsView = function (test, options) {
|
||||
var cohortsJson, cohorts, contentGroups, cohortSettings;
|
||||
var cohortsJson, cohorts, contentGroups, cohortSettings, cohortDiscussions;
|
||||
options = options || {};
|
||||
cohortsJson = options.cohorts ? {cohorts: options.cohorts} : createMockCohorts();
|
||||
cohorts = new CohortCollection(cohortsJson, {parse: true});
|
||||
@@ -78,17 +138,23 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
cohortSettings = options.cohortSettings || createMockCohortSettings(true);
|
||||
cohortSettings.url = '/mock_service/cohorts/settings';
|
||||
cohorts.url = '/mock_service/cohorts';
|
||||
|
||||
cohortDiscussions = options.cohortDiscussions || createMockCohortDiscussions();
|
||||
cohortDiscussions.url = '/mock_service/cohorts/discussion/topics';
|
||||
|
||||
requests = AjaxHelpers.requests(test);
|
||||
cohortsView = new CohortsView({
|
||||
model: cohorts,
|
||||
contentGroups: contentGroups,
|
||||
cohortSettings: cohortSettings,
|
||||
context: {
|
||||
discussionTopicsSettingsModel: cohortDiscussions,
|
||||
uploadCohortsCsvUrl: MOCK_UPLOAD_COHORTS_CSV_URL,
|
||||
studioAdvancedSettingsUrl: MOCK_STUDIO_ADVANCED_SETTINGS_URL,
|
||||
studioGroupConfigurationsUrl: MOCK_STUDIO_GROUP_CONFIGURATIONS_URL
|
||||
}
|
||||
});
|
||||
|
||||
cohortsView.render();
|
||||
if (options && options.selectCohort) {
|
||||
cohortsView.$('.cohort-select').val(options.selectCohort.toString()).change();
|
||||
@@ -195,6 +261,41 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
verifyDetailedMessage(expectedTitle, 'error', errors);
|
||||
};
|
||||
|
||||
showAndAssertDiscussionTopics = function(that) {
|
||||
|
||||
createCohortsView(that);
|
||||
|
||||
// Should see the control to toggle cohort discussions.
|
||||
expect(cohortsView.$(discussionsToggle)).not.toHaveClass('is-hidden');
|
||||
// But discussions form should not be visible until toggle is clicked.
|
||||
expect(cohortsView.$(inlineDiscussionsFormCss).length).toBe(0);
|
||||
expect(cohortsView.$(courseWideDiscussionsFormCss).length).toBe(0);
|
||||
|
||||
expect(cohortsView.$(discussionsToggle).text()).
|
||||
toContain('Specify whether discussion topics are divided by cohort');
|
||||
|
||||
cohortsView.$(discussionsToggle).click();
|
||||
// After toggle is clicked, it should be hidden.
|
||||
expect(cohortsView.$(discussionsToggle)).toHaveClass('is-hidden');
|
||||
|
||||
// Should see the course wide discussions form and its content
|
||||
courseWideDiscussionsForm = cohortsView.$(courseWideDiscussionsFormCss);
|
||||
expect(courseWideDiscussionsForm.length).toBe(1);
|
||||
|
||||
expect(courseWideDiscussionsForm.text()).
|
||||
toContain('Course-Wide Discussion Topics');
|
||||
expect(courseWideDiscussionsForm.text()).
|
||||
toContain('Select the course-wide discussion topics that you want to divide by cohort.');
|
||||
|
||||
// Should see the inline discussions form and its content
|
||||
inlineDiscussionsForm = cohortsView.$(inlineDiscussionsFormCss);
|
||||
expect(inlineDiscussionsForm.length).toBe(1);
|
||||
expect(inlineDiscussionsForm.text()).
|
||||
toContain('Content-Specific Discussion Topics');
|
||||
expect(inlineDiscussionsForm.text()).
|
||||
toContain('Specify whether content-specific discussion topics are divided by cohort.');
|
||||
};
|
||||
|
||||
unknownUserMessage = function (name) {
|
||||
return "Unknown user: " + name;
|
||||
};
|
||||
@@ -208,6 +309,10 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-group-header');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/notification');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-state');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-category');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-course-wide');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-inline');
|
||||
TemplateHelpers.installTemplate('templates/file-upload');
|
||||
});
|
||||
|
||||
@@ -221,6 +326,8 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
|
||||
// If no cohorts have been created, can't upload a CSV file.
|
||||
expect(cohortsView.$('.wrapper-cohort-supplemental')).toHaveClass('is-hidden');
|
||||
// if no cohorts have been created, can't show the link to discussion topics.
|
||||
expect(cohortsView.$('.cohort-discussions-nav')).toHaveClass('is-hidden');
|
||||
});
|
||||
|
||||
it("syncs data when membership tab is clicked", function() {
|
||||
@@ -260,6 +367,10 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
.toBe("Your file 'upload_file.txt' has been uploaded. Allow a few minutes for processing.");
|
||||
});
|
||||
|
||||
it('can show discussion topics if cohort exists', function () {
|
||||
showAndAssertDiscussionTopics(this);
|
||||
});
|
||||
|
||||
describe("Cohort Selector", function () {
|
||||
it('has no initial selection', function () {
|
||||
createCohortsView(this);
|
||||
@@ -287,23 +398,23 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
|
||||
cohortsView.$('.cohorts-state').prop('checked', true).change();
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'PUT', '/mock_service/cohorts/settings',
|
||||
createMockCohortSettingsJson(true, [], true)
|
||||
requests, 'PATCH', '/mock_service/cohorts/settings',
|
||||
{is_cohorted: true}
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
createMockCohortSettingsJson(true)
|
||||
{is_cohorted: true}
|
||||
);
|
||||
expect(cohortsView.$('.cohorts-state').prop('checked')).toBeTruthy();
|
||||
|
||||
cohortsView.$('.cohorts-state').prop('checked', false).change();
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'PUT', '/mock_service/cohorts/settings',
|
||||
createMockCohortSettingsJson(false, [], true)
|
||||
requests, 'PATCH', '/mock_service/cohorts/settings',
|
||||
{is_cohorted: false}
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
createMockCohortSettingsJson(false)
|
||||
{is_cohorted: false}
|
||||
);
|
||||
expect(cohortsView.$('.cohorts-state').prop('checked')).toBeFalsy();
|
||||
});
|
||||
@@ -960,5 +1071,379 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Discussion Topics", function() {
|
||||
var createCourseWideView, createInlineView,
|
||||
inlineView, courseWideView, assertCohortedTopics;
|
||||
|
||||
createCourseWideView = function(that) {
|
||||
createCohortsView(that);
|
||||
|
||||
courseWideView = new CohortCourseWideDiscussionsView({
|
||||
el: cohortsView.$('.cohort-discussions-nav').removeClass('is-hidden'),
|
||||
model: cohortsView.context.discussionTopicsSettingsModel,
|
||||
cohortSettings: cohortsView.cohortSettings
|
||||
});
|
||||
courseWideView.render();
|
||||
};
|
||||
|
||||
createInlineView = function(that, discussionTopicsSettingsModel) {
|
||||
createCohortsView(that);
|
||||
|
||||
inlineView = new CohortInlineDiscussionsView({
|
||||
el: cohortsView.$('.cohort-discussions-nav').removeClass('is-hidden'),
|
||||
model: discussionTopicsSettingsModel || cohortsView.context.discussionTopicsSettingsModel,
|
||||
cohortSettings: cohortsView.cohortSettings
|
||||
});
|
||||
inlineView.render();
|
||||
};
|
||||
|
||||
assertCohortedTopics = function(view, type) {
|
||||
expect(view.$('.check-discussion-subcategory-' + type).length).toBe(2);
|
||||
expect(view.$('.check-discussion-subcategory-' + type + ':checked').length).toBe(1);
|
||||
};
|
||||
|
||||
it('renders the view properly', function() {
|
||||
showAndAssertDiscussionTopics(this);
|
||||
});
|
||||
|
||||
describe("Course Wide", function() {
|
||||
|
||||
it('shows the "Save" button as disabled initially', function() {
|
||||
createCourseWideView(this);
|
||||
expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('has one cohorted and one non-cohorted topic', function() {
|
||||
createCourseWideView(this);
|
||||
|
||||
assertCohortedTopics(courseWideView, 'course-wide');
|
||||
|
||||
expect(courseWideView.$('.cohorted-text').length).toBe(2);
|
||||
expect(courseWideView.$('.cohorted-text.hidden').length).toBe(1);
|
||||
});
|
||||
|
||||
it('enables the "Save" button after changing checkbox', function() {
|
||||
createCourseWideView(this);
|
||||
|
||||
// save button is disabled.
|
||||
expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
|
||||
|
||||
$(courseWideView.$('.check-discussion-subcategory-course-wide')[0]).prop('checked', false).change();
|
||||
|
||||
// save button is enabled.
|
||||
expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('saves the topic successfully', function() {
|
||||
createCourseWideView(this);
|
||||
|
||||
$(courseWideView.$('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
|
||||
expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
|
||||
|
||||
// Save the updated settings
|
||||
courseWideView.$('.action-save').click();
|
||||
|
||||
// fake requests for cohort settings with PATCH method.
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'PATCH', '/mock_service/cohorts/settings',
|
||||
{cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
{cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
|
||||
);
|
||||
|
||||
// fake request for discussion/topics with GET method.
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'GET', '/mock_service/cohorts/discussion/topics'
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
createMockCohortDiscussions()
|
||||
);
|
||||
|
||||
// verify the success message.
|
||||
expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
|
||||
verifyMessage('Your changes have been saved.', 'confirmation');
|
||||
});
|
||||
|
||||
it('shows an appropriate message when subsequent "GET" returns HTTP500', function() {
|
||||
createCourseWideView(this);
|
||||
|
||||
$(courseWideView.$('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
|
||||
expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
|
||||
|
||||
// Save the updated settings
|
||||
courseWideView.$('.action-save').click();
|
||||
|
||||
// fake requests for cohort settings with PATCH method.
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'PATCH', '/mock_service/cohorts/settings',
|
||||
{cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
{cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
|
||||
);
|
||||
|
||||
// fake request for discussion/topics with GET method.
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'GET', '/mock_service/cohorts/discussion/topics'
|
||||
);
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
|
||||
var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
|
||||
expect(courseWideView.$('.message-title').text().trim()).toBe(expectedTitle);
|
||||
});
|
||||
|
||||
it('shows an appropriate error message for HTTP500', function () {
|
||||
createCourseWideView(this);
|
||||
|
||||
$(courseWideView.$('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
|
||||
courseWideView.$('.action-save').click();
|
||||
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
|
||||
expect(courseWideView.$('.message-title').text().trim()).toBe(expectedTitle);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Inline", function() {
|
||||
var enableSaveButton, mockGetRequest, verifySuccess, mockPatchRequest;
|
||||
|
||||
enableSaveButton = function() {
|
||||
// enable the inline discussion topics.
|
||||
inlineView.$('.check-cohort-inline-discussions').prop('checked', 'checked').change();
|
||||
|
||||
$(inlineView.$('.check-discussion-subcategory-inline')[0]).prop('checked', 'checked').change();
|
||||
|
||||
expect(inlineView.$(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
|
||||
};
|
||||
|
||||
verifySuccess = function() {
|
||||
// verify the success message.
|
||||
expect(inlineView.$(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
|
||||
verifyMessage('Your changes have been saved.', 'confirmation');
|
||||
};
|
||||
|
||||
mockPatchRequest = function(cohortedInlineDiscussions) {
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'PATCH', '/mock_service/cohorts/settings',
|
||||
{
|
||||
cohorted_inline_discussions: cohortedInlineDiscussions,
|
||||
always_cohort_inline_discussions: false
|
||||
}
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
{
|
||||
cohorted_inline_discussions: cohortedInlineDiscussions,
|
||||
always_cohort_inline_discussions: false
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
mockGetRequest = function(allCohorted) {
|
||||
// fake request for discussion/topics with GET method.
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'GET', '/mock_service/cohorts/discussion/topics'
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
createMockCohortDiscussions(allCohorted)
|
||||
);
|
||||
};
|
||||
|
||||
it('shows the "Save" button as disabled initially', function() {
|
||||
createInlineView(this);
|
||||
expect(inlineView.$(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows always cohort radio button as selected', function() {
|
||||
createInlineView(this);
|
||||
inlineView.$('.check-all-inline-discussions').prop('checked', 'checked').change();
|
||||
|
||||
// verify always cohort inline discussions is being selected.
|
||||
expect(inlineView.$('.check-all-inline-discussions').prop('checked')).toBeTruthy();
|
||||
|
||||
// verify that inline topics are disabled
|
||||
expect(inlineView.$('.check-discussion-subcategory-inline').prop('disabled')).toBeTruthy();
|
||||
expect(inlineView.$('.check-discussion-category').prop('disabled')).toBeTruthy();
|
||||
|
||||
// verify that cohort some topics are not being selected.
|
||||
expect(inlineView.$('.check-cohort-inline-discussions').prop('checked')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('shows cohort some topics radio button as selected', function() {
|
||||
createInlineView(this);
|
||||
inlineView.$('.check-cohort-inline-discussions').prop('checked', 'checked').change();
|
||||
|
||||
// verify some cohort inline discussions radio is being selected.
|
||||
expect(inlineView.$('.check-cohort-inline-discussions').prop('checked')).toBeTruthy();
|
||||
|
||||
// verify always cohort radio is not selected.
|
||||
expect(inlineView.$('.check-all-inline-discussions').prop('checked')).toBeFalsy();
|
||||
|
||||
// verify that inline topics are enabled
|
||||
expect(inlineView.$('.check-discussion-subcategory-inline').prop('disabled')).toBeFalsy();
|
||||
expect(inlineView.$('.check-discussion-category').prop('disabled')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('has cohorted and non-cohorted topics', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
assertCohortedTopics(inlineView, 'inline');
|
||||
});
|
||||
|
||||
it('enables "Save" button after changing from always inline option', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
});
|
||||
|
||||
it('saves the topic', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
|
||||
// Save the updated settings
|
||||
inlineView.$('.action-save').click();
|
||||
|
||||
mockPatchRequest(['Inline_Discussion_1']);
|
||||
mockGetRequest();
|
||||
|
||||
verifySuccess();
|
||||
});
|
||||
|
||||
it('selects the parent category when all children are selected', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
|
||||
// parent category should be indeterminate.
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
|
||||
expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
|
||||
|
||||
inlineView.$('.check-discussion-subcategory-inline').prop('checked', 'checked').change();
|
||||
// parent should be checked as we checked all children
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(1);
|
||||
});
|
||||
|
||||
it('selects/deselects all children when a parent category is selected/deselected', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
|
||||
|
||||
inlineView.$('.check-discussion-category').prop('checked', 'checked').change();
|
||||
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(1);
|
||||
expect(inlineView.$('.check-discussion-subcategory-inline:checked').length).toBe(2);
|
||||
|
||||
// un-check the parent, all children should be unchecd.
|
||||
inlineView.$('.check-discussion-category').prop('checked', false).change();
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
|
||||
expect(inlineView.$('.check-discussion-subcategory-inline:checked').length).toBe(0);
|
||||
});
|
||||
|
||||
it('saves correctly when a subset of topics are selected within a category', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
|
||||
// parent category should be indeterminate.
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
|
||||
expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
|
||||
|
||||
// Save the updated settings
|
||||
inlineView.$('.action-save').click();
|
||||
|
||||
mockPatchRequest(['Inline_Discussion_1']);
|
||||
mockGetRequest();
|
||||
|
||||
verifySuccess();
|
||||
// parent category should be indeterminate.
|
||||
expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
|
||||
});
|
||||
|
||||
it('saves correctly when all child topics are selected within a category', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
|
||||
// parent category should be indeterminate.
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
|
||||
expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
|
||||
|
||||
inlineView.$('.check-discussion-subcategory-inline').prop('checked', 'checked').change();
|
||||
// Save the updated settings
|
||||
inlineView.$('.action-save').click();
|
||||
|
||||
mockPatchRequest(['Inline_Discussion_1', 'Inline_Discussion_2']);
|
||||
mockGetRequest(true);
|
||||
|
||||
verifySuccess();
|
||||
// parent category should be checked.
|
||||
expect(inlineView.$('.check-discussion-category:checked').length).toBe(1);
|
||||
});
|
||||
|
||||
it('shows an appropriate message when no inline topics exist', function() {
|
||||
|
||||
var topicsJson, discussionTopicsSettingsModel;
|
||||
|
||||
topicsJson = {
|
||||
course_wide_discussions: {
|
||||
children: ['Topic_C_1'],
|
||||
entries: {
|
||||
Topic_C_1: {
|
||||
sort_key: null,
|
||||
is_cohorted: true,
|
||||
id: 'Topic_C_1'
|
||||
}
|
||||
}
|
||||
},
|
||||
inline_discussions: {
|
||||
subcategories: {},
|
||||
children: []
|
||||
}
|
||||
};
|
||||
discussionTopicsSettingsModel = new DiscussionTopicsSettingsModel(topicsJson);
|
||||
|
||||
createInlineView(this, discussionTopicsSettingsModel);
|
||||
|
||||
var expectedTitle = "No content-specific discussion topics exist.";
|
||||
expect(inlineView.$('.no-topics').text().trim()).toBe(expectedTitle);
|
||||
});
|
||||
|
||||
it('shows an appropriate message when subsequent "GET" returns HTTP500', function() {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
|
||||
// Save the updated settings
|
||||
inlineView.$('.action-save').click();
|
||||
|
||||
mockPatchRequest(['Inline_Discussion_1']);
|
||||
|
||||
// fake request for discussion/topics with GET method.
|
||||
AjaxHelpers.expectJsonRequest(
|
||||
requests, 'GET', '/mock_service/cohorts/discussion/topics'
|
||||
);
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
|
||||
var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
|
||||
expect(inlineView.$('.message-title').text().trim()).toBe(expectedTitle);
|
||||
});
|
||||
|
||||
it('shows an appropriate error message for HTTP500', function () {
|
||||
createInlineView(this);
|
||||
enableSaveButton();
|
||||
|
||||
$(inlineView.$('.check-discussion-subcategory-inline')[1]).prop('checked', 'checked').change();
|
||||
inlineView.$('.action-save').click();
|
||||
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
|
||||
expect(inlineView.$('.message-title').text().trim()).toBe(expectedTitle);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
'history': 'js/vendor/history',
|
||||
'js/verify_student/photocapture': 'js/verify_student/photocapture',
|
||||
'js/staff_debug_actions': 'js/staff_debug_actions',
|
||||
'js/vendor/jquery.qubit': 'js/vendor/jquery.qubit',
|
||||
|
||||
// Backbone classes loaded explicitly until they are converted to use RequireJS
|
||||
'js/models/notification': 'js/models/notification',
|
||||
@@ -68,6 +69,10 @@
|
||||
'js/groups/models/cohort': 'js/groups/models/cohort',
|
||||
'js/groups/models/content_group': 'js/groups/models/content_group',
|
||||
'js/groups/models/course_cohort_settings': 'js/groups/models/course_cohort_settings',
|
||||
'js/groups/models/cohort_discussions': 'js/groups/models/cohort_discussions',
|
||||
'js/groups/views/cohort_discussions': 'js/groups/views/cohort_discussions',
|
||||
'js/groups/views/cohort_discussions_course_wide': 'js/groups/views/cohort_discussions_course_wide',
|
||||
'js/groups/views/cohort_discussions_inline': 'js/groups/views/cohort_discussions_inline',
|
||||
'js/groups/views/course_cohort_settings_notification': 'js/groups/views/course_cohort_settings_notification',
|
||||
'js/groups/collections/cohort': 'js/groups/collections/cohort',
|
||||
'js/groups/views/cohort_editor': 'js/groups/views/cohort_editor',
|
||||
@@ -300,6 +305,22 @@
|
||||
exports: 'edx.groups.CourseCohortSettingsModel',
|
||||
deps: ['backbone']
|
||||
},
|
||||
'js/groups/models/cohort_discussions': {
|
||||
exports: 'edx.groups.DiscussionTopicsSettingsModel',
|
||||
deps: ['backbone']
|
||||
},
|
||||
'js/groups/views/cohort_discussions': {
|
||||
exports: 'edx.groups.CohortDiscussionConfigurationView',
|
||||
deps: ['backbone']
|
||||
},
|
||||
'js/groups/views/cohort_discussions_course_wide': {
|
||||
exports: 'edx.groups.CourseWideDiscussionsView',
|
||||
deps: ['backbone', 'js/groups/views/cohort_discussions']
|
||||
},
|
||||
'js/groups/views/cohort_discussions_inline': {
|
||||
exports: 'edx.groups.InlineDiscussionsView',
|
||||
deps: ['backbone', 'js/groups/views/cohort_discussions', 'js/vendor/jquery.qubit']
|
||||
},
|
||||
'js/groups/views/course_cohort_settings_notification': {
|
||||
exports: 'edx.groups.CourseCohortSettingsNotificationView',
|
||||
deps: ['backbone']
|
||||
|
||||
97
lms/static/js/vendor/jquery.qubit.js
vendored
Executable file
97
lms/static/js/vendor/jquery.qubit.js
vendored
Executable file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
** Checkboxes TreeView- jQuery
|
||||
** https://github.com/aexmachina/jquery-qubit
|
||||
**
|
||||
** Copyright (c) 2014 Simon Wade
|
||||
** The MIT License (MIT)
|
||||
** https://github.com/aexmachina/jquery-qubit/blob/master/LICENSE.txt
|
||||
**
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.fn.qubit = function(options) {
|
||||
return this.each(function() {
|
||||
var qubit = new Qubit(this, options);
|
||||
});
|
||||
};
|
||||
var Qubit = function(el) {
|
||||
var self = this;
|
||||
this.scope = $(el);
|
||||
this.scope.on('change', 'input[type=checkbox]', function(e) {
|
||||
if (!self.suspendListeners) {
|
||||
self.process(e.target);
|
||||
}
|
||||
});
|
||||
this.scope.find('input[type=checkbox]:checked').each(function() {
|
||||
self.process(this);
|
||||
});
|
||||
};
|
||||
Qubit.prototype = {
|
||||
itemSelector: 'li',
|
||||
process: function(checkbox) {
|
||||
var checkbox = $(checkbox),
|
||||
parentItems = checkbox.parentsUntil(this.scope, this.itemSelector);
|
||||
try {
|
||||
this.suspendListeners = true;
|
||||
// all children inherit my state
|
||||
parentItems.eq(0).find('input[type=checkbox]')
|
||||
.filter(checkbox.prop('checked') ? ':not(:checked)' : ':checked')
|
||||
.each(function() {
|
||||
if (!$(this).parent().hasClass('hidden')) {
|
||||
$(this).prop('checked', checkbox.prop('checked'));
|
||||
}
|
||||
})
|
||||
.trigger('change');
|
||||
this.processParents(checkbox);
|
||||
} finally {
|
||||
this.suspendListeners = false;
|
||||
}
|
||||
},
|
||||
processParents: function() {
|
||||
var self = this, changed = false;
|
||||
this.scope.find('input[type=checkbox]').each(function() {
|
||||
var $this = $(this),
|
||||
parent = $this.closest(self.itemSelector),
|
||||
children = parent.find('input[type=checkbox]').not($this),
|
||||
numChecked = children.filter(function() {
|
||||
return $(this).prop('checked') || $(this).prop('indeterminate');
|
||||
}).length;
|
||||
|
||||
if (children.length) {
|
||||
if (numChecked == 0) {
|
||||
if (self.setChecked($this, false)) changed = true;
|
||||
} else if (numChecked == children.length) {
|
||||
if (self.setChecked($this, true)) changed = true;
|
||||
} else {
|
||||
if (self.setIndeterminate($this, true)) changed = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (self.setIndeterminate($this, false)) changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) this.processParents();
|
||||
},
|
||||
setChecked: function(checkbox, value, event) {
|
||||
var changed = false;
|
||||
if (checkbox.prop('indeterminate')) {
|
||||
checkbox.prop('indeterminate', false);
|
||||
changed = true;
|
||||
}
|
||||
if (checkbox.prop('checked') != value) {
|
||||
checkbox.prop('checked', value).trigger('change');
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
},
|
||||
setIndeterminate: function(checkbox, value) {
|
||||
if (value) {
|
||||
checkbox.prop('checked', false);
|
||||
}
|
||||
if (checkbox.prop('indeterminate') != value) {
|
||||
checkbox.prop('indeterminate', value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}(jQuery));
|
||||
Reference in New Issue
Block a user