Support adding cohorts from the instructor dashboard
TNL-162
This commit is contained in:
@@ -1,11 +1,42 @@
|
||||
(function(Backbone) {
|
||||
var NotificationModel = Backbone.Model.extend({
|
||||
defaults: {
|
||||
// Supported types are "confirmation" and "error".
|
||||
/**
|
||||
* The type of notification to be shown.
|
||||
* Supported types are "confirmation", "warning" and "error".
|
||||
*/
|
||||
type: "confirmation",
|
||||
/**
|
||||
* The title to be shown for the notification. This string should be short so
|
||||
* that it can be shown on a single line.
|
||||
*/
|
||||
title: "",
|
||||
/**
|
||||
* An optional message giving more details for the notification. This string can be as long
|
||||
* as needed and will wrap.
|
||||
*/
|
||||
message: "",
|
||||
/**
|
||||
* An optional array of detail messages to be shown beneath the title and message. This is
|
||||
* typically used to enumerate a set of warning or error conditions that occurred.
|
||||
*/
|
||||
details: [],
|
||||
actionText: "",
|
||||
/**
|
||||
* The text label to be shown for an action button, or null if there is no associated action.
|
||||
*/
|
||||
actionText: null,
|
||||
/**
|
||||
* The class to be added to the action button. This allows selectors to be written that can
|
||||
* target the action button directly.
|
||||
*/
|
||||
actionClass: "",
|
||||
/**
|
||||
* An optional icon class to be shown before the text on the action button.
|
||||
*/
|
||||
actionIconClass: "",
|
||||
/**
|
||||
* An optional callback that will be invoked when the user clicks on the action button.
|
||||
*/
|
||||
actionCallback: null
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,27 +3,28 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
function (Backbone, $, AjaxHelpers, TemplateHelpers, CohortsView, CohortCollection) {
|
||||
describe("Cohorts View", function () {
|
||||
var catLoversInitialCount = 123, dogLoversInitialCount = 456, unknownUserMessage,
|
||||
createMockCohorts, createCohortsView, cohortsView, requests, verifyMessage, verifyHeader;
|
||||
createMockCohort, createMockCohorts, createCohortsView, cohortsView, requests, respondToRefresh,
|
||||
verifyMessage, verifyNoMessage, verifyDetailedMessage, verifyHeader;
|
||||
|
||||
createMockCohort = function (name, id, user_count) {
|
||||
return {
|
||||
id: id || 1,
|
||||
name: name,
|
||||
user_count: user_count || 0
|
||||
};
|
||||
};
|
||||
|
||||
createMockCohorts = function (catCount, dogCount) {
|
||||
return {
|
||||
cohorts: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Cat Lovers',
|
||||
user_count: catCount || catLoversInitialCount
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Dog Lovers',
|
||||
user_count: dogCount || dogLoversInitialCount
|
||||
}
|
||||
createMockCohort('Cat Lovers', 1, catCount || catLoversInitialCount),
|
||||
createMockCohort('Dog Lovers', 2, dogCount || dogLoversInitialCount)
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
createCohortsView = function (test, initialCohortID) {
|
||||
var cohorts = new CohortCollection(createMockCohorts(), {parse: true});
|
||||
createCohortsView = function (test, initialCohortID, initialCohorts) {
|
||||
var cohorts = new CohortCollection(initialCohorts || createMockCohorts(), {parse: true});
|
||||
cohorts.url = '/mock_service';
|
||||
requests = AjaxHelpers.requests(test);
|
||||
cohortsView = new CohortsView({
|
||||
@@ -31,32 +32,41 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
});
|
||||
cohortsView.render();
|
||||
if (initialCohortID) {
|
||||
cohortsView.$('.cohort-select').val(initialCohortID).change();
|
||||
cohortsView.$('.cohort-select').val(initialCohortID.toString()).change();
|
||||
}
|
||||
};
|
||||
|
||||
verifyMessage = function(expectedTitle, messageType, expectedDetails, expectedAction) {
|
||||
var numDetails = cohortsView.$('.summary-items').children().length;
|
||||
respondToRefresh = function(catCount, dogCount) {
|
||||
AjaxHelpers.respondWithJson(requests, createMockCohorts(catCount, dogCount));
|
||||
};
|
||||
|
||||
verifyMessage = function(expectedTitle, expectedMessageType, expectedAction, hasDetails) {
|
||||
expect(cohortsView.$('.message-title').text().trim()).toBe(expectedTitle);
|
||||
expect(cohortsView.$('div.message').hasClass('message-' + messageType)).toBe(true);
|
||||
expect(cohortsView.$('div.message')).toHaveClass('message-' + expectedMessageType);
|
||||
if (expectedAction) {
|
||||
expect(cohortsView.$('.message-actions .action-primary').text()).toBe(expectedAction);
|
||||
expect(cohortsView.$('.message-actions .action-primary').text().trim()).toBe(expectedAction);
|
||||
}
|
||||
else {
|
||||
expect(cohortsView.$('.message-actions .action-primary').length).toBe(0);
|
||||
}
|
||||
if (expectedDetails) {
|
||||
expect(numDetails).toBe(expectedDetails.length);
|
||||
cohortsView.$('.summary-item').each(function (index) {
|
||||
expect($(this).text().trim()).toBe(expectedDetails[index]);
|
||||
});
|
||||
}
|
||||
else {
|
||||
expect(numDetails).toBe(0);
|
||||
if (!hasDetails) {
|
||||
expect(cohortsView.$('.summary-items').length).toBe(0);
|
||||
}
|
||||
};
|
||||
|
||||
verifyNoMessage = function() {
|
||||
expect(cohortsView.$('.message').length).toBe(0);
|
||||
};
|
||||
|
||||
verifyDetailedMessage = function(expectedTitle, expectedMessageType, expectedDetails, expectedAction) {
|
||||
var numDetails = cohortsView.$('.summary-items').children().length;
|
||||
verifyMessage(expectedTitle, expectedMessageType, expectedAction, true);
|
||||
expect(numDetails).toBe(expectedDetails.length);
|
||||
cohortsView.$('.summary-item').each(function (index) {
|
||||
expect($(this).text().trim()).toBe(expectedDetails[index]);
|
||||
});
|
||||
};
|
||||
|
||||
verifyHeader = function(expectedCohortId, expectedTitle, expectedCount) {
|
||||
var header = cohortsView.$('.cohort-management-group-header');
|
||||
expect(cohortsView.$('.cohort-select').val()).toBe(expectedCohortId.toString());
|
||||
@@ -83,11 +93,21 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
beforeEach(function () {
|
||||
setFixtures("<div></div>");
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohorts');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/add-cohort-form');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-selector');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-editor');
|
||||
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/notification');
|
||||
});
|
||||
|
||||
it("Show an error if no cohorts are defined", function() {
|
||||
createCohortsView(this, null, { cohorts: [] });
|
||||
verifyMessage(
|
||||
'You currently have no cohort groups configured',
|
||||
'warning',
|
||||
'Add Cohort Group'
|
||||
);
|
||||
});
|
||||
|
||||
describe("Cohort Selector", function () {
|
||||
it('has no initial selection', function () {
|
||||
createCohortsView(this);
|
||||
@@ -96,19 +116,158 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
});
|
||||
|
||||
it('can select a cohort', function () {
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
verifyHeader(1, 'Cat Lovers', catLoversInitialCount);
|
||||
});
|
||||
|
||||
it('can switch cohort', function () {
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
cohortsView.$('.cohort-select').val("2").change();
|
||||
verifyHeader(2, 'Dog Lovers', dogLoversInitialCount);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Add Cohorts Form", function () {
|
||||
var defaultCohortName = 'New Cohort';
|
||||
|
||||
it("can add a cohort", function() {
|
||||
createCohortsView(this, null, { cohorts: [] });
|
||||
cohortsView.$('.action-create').click();
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(1);
|
||||
expect(cohortsView.$('.cohort-management-nav')).toHaveClass('is-disabled');
|
||||
expect(cohortsView.$('.cohort-management-group')).toHaveClass('is-hidden');
|
||||
cohortsView.$('.cohort-create-name').val(defaultCohortName);
|
||||
cohortsView.$('.action-save').click();
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/add', 'name=New+Cohort');
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
{
|
||||
success: true,
|
||||
cohort: { id: 1, name: defaultCohortName }
|
||||
}
|
||||
);
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
{ cohorts: createMockCohort(defaultCohortName) }
|
||||
);
|
||||
verifyMessage(
|
||||
'The ' + defaultCohortName + ' cohort group has been created.' +
|
||||
' You can manually add students to this group below.',
|
||||
'confirmation'
|
||||
);
|
||||
verifyHeader(1, defaultCohortName, 0);
|
||||
expect(cohortsView.$('.cohort-management-nav')).not.toHaveClass('is-disabled');
|
||||
expect(cohortsView.$('.cohort-management-group')).not.toHaveClass('is-hidden');
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(0);
|
||||
});
|
||||
|
||||
it("trims off whitespace before adding a cohort", function() {
|
||||
createCohortsView(this);
|
||||
cohortsView.$('.action-create').click();
|
||||
cohortsView.$('.cohort-create-name').val(' New Cohort ');
|
||||
cohortsView.$('.action-save').click();
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/add', 'name=New+Cohort');
|
||||
});
|
||||
|
||||
it("does not allow a blank cohort name to be submitted", function() {
|
||||
createCohortsView(this, 1);
|
||||
cohortsView.$('.action-create').click();
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(1);
|
||||
cohortsView.$('.cohort-create-name').val('');
|
||||
expect(cohortsView.$('.cohort-management-nav')).toHaveClass('is-disabled');
|
||||
cohortsView.$('.action-save').click();
|
||||
expect(requests.length).toBe(0);
|
||||
verifyMessage('Please enter a name for your new cohort group.', 'error');
|
||||
});
|
||||
|
||||
it("shows a message when adding a cohort throws a server error", function() {
|
||||
createCohortsView(this, 1);
|
||||
cohortsView.$('.action-create').click();
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(1);
|
||||
cohortsView.$('.cohort-create-name').val(defaultCohortName);
|
||||
cohortsView.$('.action-save').click();
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/add', 'name=New+Cohort');
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
verifyHeader(1, 'Cat Lovers', catLoversInitialCount);
|
||||
verifyMessage(
|
||||
"We've encountered an error. Please refresh your browser and then try again.",
|
||||
'error'
|
||||
);
|
||||
});
|
||||
|
||||
it("shows a server message if adding a cohort fails", function() {
|
||||
createCohortsView(this, 1);
|
||||
cohortsView.$('.action-create').click();
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(1);
|
||||
cohortsView.$('.cohort-create-name').val('Cat Lovers');
|
||||
cohortsView.$('.action-save').click();
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/add', 'name=Cat+Lovers');
|
||||
AjaxHelpers.respondWithJson(
|
||||
requests,
|
||||
{
|
||||
success: false,
|
||||
msg: 'You cannot create two cohorts with the same name'
|
||||
}
|
||||
);
|
||||
verifyHeader(1, 'Cat Lovers', catLoversInitialCount);
|
||||
verifyMessage('You cannot create two cohorts with the same name', 'error');
|
||||
});
|
||||
|
||||
it("is removed when 'Cancel' is clicked", function() {
|
||||
createCohortsView(this, 1);
|
||||
cohortsView.$('.action-create').click();
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(1);
|
||||
expect(cohortsView.$('.cohort-management-nav')).toHaveClass('is-disabled');
|
||||
cohortsView.$('.action-cancel').click();
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(0);
|
||||
expect(cohortsView.$('.cohort-management-nav')).not.toHaveClass('is-disabled');
|
||||
});
|
||||
|
||||
it("shows an error if canceled when no cohorts are defined", function() {
|
||||
createCohortsView(this, null, { cohorts: [] });
|
||||
cohortsView.$('.action-create').click();
|
||||
expect(cohortsView.$('.cohort-management-create-form').length).toBe(1);
|
||||
expect(cohortsView.$('.cohort-management-nav')).toHaveClass('is-disabled');
|
||||
cohortsView.$('.action-cancel').click();
|
||||
verifyMessage(
|
||||
'You currently have no cohort groups configured',
|
||||
'warning',
|
||||
'Add Cohort Group'
|
||||
);
|
||||
});
|
||||
|
||||
it("hides any error message when switching to show a cohort", function() {
|
||||
createCohortsView(this, 1);
|
||||
|
||||
// First try to save a blank name to create a message
|
||||
cohortsView.$('.action-create').click();
|
||||
cohortsView.$('.cohort-create-name').val('');
|
||||
cohortsView.$('.action-save').click();
|
||||
verifyMessage('Please enter a name for your new cohort group.', 'error');
|
||||
|
||||
// Now switch to a different cohort
|
||||
cohortsView.$('.cohort-select').val("2").change();
|
||||
verifyHeader(2, 'Dog Lovers', dogLoversInitialCount);
|
||||
verifyNoMessage();
|
||||
});
|
||||
|
||||
it("hides any error message when canceling the form", function() {
|
||||
createCohortsView(this, 1);
|
||||
|
||||
// First try to save a blank name to create a message
|
||||
cohortsView.$('.action-create').click();
|
||||
cohortsView.$('.cohort-create-name').val('');
|
||||
cohortsView.$('.action-save').click();
|
||||
verifyMessage('Please enter a name for your new cohort group.', 'error');
|
||||
|
||||
// Now cancel the form
|
||||
cohortsView.$('.action-cancel').click();
|
||||
verifyNoMessage();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Add Students Button", function () {
|
||||
var getStudentInput, addStudents, respondToAdd, respondToRefresh;
|
||||
var getStudentInput, addStudents, respondToAdd;
|
||||
|
||||
getStudentInput = function() {
|
||||
return cohortsView.$('.cohort-management-group-add-students');
|
||||
@@ -126,12 +285,8 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
);
|
||||
};
|
||||
|
||||
respondToRefresh = function(catCount, dogCount) {
|
||||
AjaxHelpers.respondWithJson(requests, createMockCohorts(catCount, dogCount));
|
||||
};
|
||||
|
||||
it('shows an error when adding with no students specified', function() {
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
addStudents(' ');
|
||||
expect(requests.length).toBe(0);
|
||||
verifyMessage('Please enter a username or email.', 'error');
|
||||
@@ -140,7 +295,7 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
|
||||
it('can add a single student', function() {
|
||||
var catLoversUpdatedCount = catLoversInitialCount + 1;
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
addStudents('student@sample.com');
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/1/add', 'users=student%40sample.com');
|
||||
respondToAdd({ added: ['student@sample.com'] });
|
||||
@@ -151,13 +306,13 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
});
|
||||
|
||||
it('shows an error when adding a student that does not exist', function() {
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
addStudents('unknown@sample.com');
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/1/add', 'users=unknown%40sample.com');
|
||||
respondToAdd({ unknown: ['unknown@sample.com'] });
|
||||
respondToRefresh(catLoversInitialCount, dogLoversInitialCount);
|
||||
verifyHeader(1, 'Cat Lovers', catLoversInitialCount);
|
||||
verifyMessage('There was an error when trying to add students:', 'error',
|
||||
verifyDetailedMessage('There was an error when trying to add students:', 'error',
|
||||
[unknownUserMessage('unknown@sample.com')]
|
||||
);
|
||||
expect(getStudentInput().val()).toBe('unknown@sample.com');
|
||||
@@ -165,11 +320,11 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
|
||||
it('shows a "view all" button when more than 5 students do not exist', function() {
|
||||
var sixUsers = 'unknown1@sample.com, unknown2@sample.com, unknown3@sample.com, unknown4@sample.com, unknown5@sample.com, unknown6@sample.com';
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
|
||||
addStudents(sixUsers);
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/1/add',
|
||||
'users=' + sixUsers.replace(/@/g, "%40").replace(/, /g, "%2C+")
|
||||
'users=' + sixUsers.replace(/@/g, "%40").replace(/, /g, "%2C+")
|
||||
);
|
||||
respondToAdd({ unknown: [
|
||||
'unknown1@sample.com',
|
||||
@@ -180,7 +335,7 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
'unknown6@sample.com']
|
||||
});
|
||||
respondToRefresh(catLoversInitialCount + 6, dogLoversInitialCount);
|
||||
verifyMessage('There were 6 errors when trying to add students:', 'error',
|
||||
verifyDetailedMessage('There were 6 errors when trying to add students:', 'error',
|
||||
[
|
||||
unknownUserMessage('unknown1@sample.com'), unknownUserMessage('unknown2@sample.com'),
|
||||
unknownUserMessage('unknown3@sample.com'), unknownUserMessage('unknown4@sample.com'),
|
||||
@@ -190,8 +345,8 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
);
|
||||
expect(getStudentInput().val()).toBe(sixUsers);
|
||||
// Click "View all"
|
||||
cohortsView.$('a.action-primary').click();
|
||||
verifyMessage('There were 6 errors when trying to add students:', 'error',
|
||||
cohortsView.$('.action-expand').click();
|
||||
verifyDetailedMessage('There were 6 errors when trying to add students:', 'error',
|
||||
[
|
||||
unknownUserMessage('unknown1@sample.com'), unknownUserMessage('unknown2@sample.com'),
|
||||
unknownUserMessage('unknown3@sample.com'), unknownUserMessage('unknown4@sample.com'),
|
||||
@@ -202,11 +357,11 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
|
||||
it('shows students moved from one cohort to another', function() {
|
||||
var sixUsers = 'moved1@sample.com, moved2@sample.com, moved3@sample.com, alreadypresent@sample.com';
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
|
||||
addStudents(sixUsers);
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/mock_service/1/add',
|
||||
'users=' + sixUsers.replace(/@/g, "%40").replace(/, /g, "%2C+")
|
||||
'users=' + sixUsers.replace(/@/g, "%40").replace(/, /g, "%2C+")
|
||||
);
|
||||
respondToAdd({
|
||||
changed: [
|
||||
@@ -218,7 +373,7 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
});
|
||||
respondToRefresh();
|
||||
|
||||
verifyMessage('3 students have been added to this cohort group', 'confirmation',
|
||||
verifyDetailedMessage('3 students have been added to this cohort group', 'confirmation',
|
||||
[
|
||||
"2 students were removed from group 2",
|
||||
"1 student was removed from group 3",
|
||||
@@ -229,7 +384,7 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
});
|
||||
|
||||
it('shows a message when the add fails', function() {
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
addStudents('student@sample.com');
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
verifyMessage('Error adding students.', 'error');
|
||||
@@ -237,7 +392,7 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
|
||||
});
|
||||
|
||||
it('clears an error message on subsequent add', function() {
|
||||
createCohortsView(this, "1");
|
||||
createCohortsView(this, 1);
|
||||
|
||||
// First verify that an error is shown
|
||||
addStudents('student@sample.com');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification', 'js/common_helpers/template_helpers'],
|
||||
function (Backbone, $, NotificationModel, NotificationView, TemplateHelpers) {
|
||||
describe("NotificationView", function () {
|
||||
var createNotification, verifyTitle, verifyDetails, verifyAction, notificationView;
|
||||
var createNotification, verifyTitle, verifyMessage, verifyDetails, verifyAction, notificationView;
|
||||
|
||||
createNotification = function (modelVals) {
|
||||
var notificationModel = new NotificationModel(modelVals);
|
||||
@@ -16,6 +16,10 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
|
||||
expect(notificationView.$('.message-title').text().trim()).toBe(expectedTitle);
|
||||
};
|
||||
|
||||
verifyMessage = function (expectedMessage) {
|
||||
expect(notificationView.$('.message-copy').text().trim()).toBe(expectedMessage);
|
||||
};
|
||||
|
||||
verifyDetails = function (expectedDetails) {
|
||||
var details = notificationView.$('.summary-item');
|
||||
expect(details.length).toBe(expectedDetails.length);
|
||||
@@ -41,7 +45,7 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
|
||||
|
||||
it('has default values', function () {
|
||||
createNotification({});
|
||||
expect(notificationView.$('div.message').hasClass('message-confirmation')).toBe(true);
|
||||
expect(notificationView.$('div.message')).toHaveClass('message-confirmation');
|
||||
verifyTitle('');
|
||||
verifyDetails([]);
|
||||
verifyAction(null);
|
||||
@@ -49,8 +53,8 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
|
||||
|
||||
it('can use an error type', function () {
|
||||
createNotification({type: 'error'});
|
||||
expect(notificationView.$('div.message').hasClass('message-error')).toBe(true);
|
||||
expect(notificationView.$('div.message').hasClass('message-confirmation')).toBe(false);
|
||||
expect(notificationView.$('div.message')).toHaveClass('message-error');
|
||||
expect(notificationView.$('div.message')).not.toHaveClass('message-confirmation');
|
||||
});
|
||||
|
||||
it('can specify a title', function () {
|
||||
@@ -58,6 +62,11 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
|
||||
verifyTitle('notification title');
|
||||
});
|
||||
|
||||
it('can specify a message', function () {
|
||||
createNotification({message: 'This is a dummy message'});
|
||||
verifyMessage('This is a dummy message');
|
||||
});
|
||||
|
||||
it('can specify details', function () {
|
||||
var expectedDetails = ['detail 1', 'detail 2'];
|
||||
createNotification({details: expectedDetails});
|
||||
@@ -69,9 +78,9 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
|
||||
verifyAction('action text');
|
||||
});
|
||||
|
||||
it ('does not show an action button if callback is not provided', function () {
|
||||
it ('shows an action button if only text is provided', function () {
|
||||
createNotification({actionText: 'action text'});
|
||||
verifyAction(null);
|
||||
verifyAction('action text');
|
||||
});
|
||||
|
||||
it ('does not show an action button if text is not provided', function () {
|
||||
@@ -82,7 +91,7 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
|
||||
it ('triggers the callback when the action button is clicked', function () {
|
||||
var actionCallback = jasmine.createSpy('Spy on callback');
|
||||
var view = createNotification({actionText: 'action text', actionCallback: actionCallback});
|
||||
notificationView.$('a.action-primary').click();
|
||||
notificationView.$('button.action-primary').click();
|
||||
expect(actionCallback).toHaveBeenCalledWith(view);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -101,7 +101,8 @@
|
||||
|
||||
addNotifications: function(modifiedUsers) {
|
||||
var oldCohort, title, details, numPresent, numUsersAdded, numErrors,
|
||||
createErrorDetails, errorActionCallback, errorModel;
|
||||
createErrorDetails, errorActionCallback, errorModel,
|
||||
errorLimit = 5;
|
||||
|
||||
// Show confirmation messages.
|
||||
this.undelegateViewEvents(this.confirmationNotifications);
|
||||
@@ -165,7 +166,7 @@
|
||||
numErrors = modifiedUsers.unknown.length;
|
||||
if (numErrors > 0) {
|
||||
createErrorDetails = function (unknownUsers, showAllErrors) {
|
||||
var numErrors = unknownUsers.length, details = [], errorLimit = 5;
|
||||
var numErrors = unknownUsers.length, details = [];
|
||||
|
||||
for (var i = 0; i < (showAllErrors ? numErrors : Math.min(errorLimit, numErrors)); i++) {
|
||||
details.push(interpolate_text(gettext("Unknown user: {user}"), {user: unknownUsers[i]}));
|
||||
@@ -181,15 +182,16 @@
|
||||
details = createErrorDetails(modifiedUsers.unknown, false);
|
||||
|
||||
errorActionCallback = function (view) {
|
||||
view.model.set("actionCallback", null);
|
||||
view.model.set("actionText", null);
|
||||
view.model.set("details", createErrorDetails(modifiedUsers.unknown, true));
|
||||
view.render();
|
||||
};
|
||||
|
||||
errorModel = new NotificationModel({
|
||||
details: details,
|
||||
actionText: gettext("View all errors"),
|
||||
actionCallback: numErrors > 5 ? errorActionCallback : null
|
||||
actionText: numErrors > errorLimit ? gettext("View all errors") : null,
|
||||
actionCallback: errorActionCallback,
|
||||
actionClass: 'action-expand'
|
||||
});
|
||||
|
||||
this.showErrorMessage(title, false, errorModel);
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
(function(Backbone, CohortEditorView) {
|
||||
var CohortsView = Backbone.View.extend({
|
||||
(function($, _, Backbone, gettext, interpolate_text, CohortEditorView, NotificationModel, NotificationView) {
|
||||
var hiddenClass = 'is-hidden',
|
||||
disabledClass = 'is-disabled';
|
||||
|
||||
this.CohortsView = Backbone.View.extend({
|
||||
events : {
|
||||
"change .cohort-select": "showCohortEditor"
|
||||
'change .cohort-select': 'onCohortSelected',
|
||||
'click .action-create': 'showAddCohortForm',
|
||||
'click .action-cancel': 'cancelAddCohortForm',
|
||||
'click .action-save': 'saveAddCohortForm'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.template = _.template($('#cohorts-tpl').text());
|
||||
this.selectorTemplate = _.template($('#cohort-selector-tpl').text());
|
||||
this.addCohortFormTemplate = _.template($('#add-cohort-form-tpl').text());
|
||||
this.advanced_settings_url = options.advanced_settings_url;
|
||||
this.model.on('sync', this.onSync, this);
|
||||
},
|
||||
@@ -15,7 +22,7 @@
|
||||
this.$el.html(this.template({
|
||||
cohorts: this.model.models
|
||||
}));
|
||||
this.renderSelector();
|
||||
this.onSync();
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -27,7 +34,25 @@
|
||||
},
|
||||
|
||||
onSync: function() {
|
||||
this.renderSelector(this.getSelectedCohort());
|
||||
var selectedCohort = this.lastSelectedCohortId && this.model.get(this.lastSelectedCohortId),
|
||||
hasCohorts = this.model.length > 0;
|
||||
this.hideAddCohortForm();
|
||||
if (hasCohorts) {
|
||||
this.$('.cohort-management-nav').removeClass(hiddenClass);
|
||||
this.renderSelector(selectedCohort);
|
||||
if (selectedCohort) {
|
||||
this.showCohortEditor(selectedCohort);
|
||||
}
|
||||
} else {
|
||||
this.$('.cohort-management-nav').addClass(hiddenClass);
|
||||
this.showNotification({
|
||||
type: 'warning',
|
||||
title: gettext('You currently have no cohort groups configured'),
|
||||
actionText: gettext('Add Cohort Group'),
|
||||
actionClass: 'action-create',
|
||||
actionIconClass: 'icon-plus'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getSelectedCohort: function() {
|
||||
@@ -35,22 +60,116 @@
|
||||
return id && this.model.get(parseInt(id));
|
||||
},
|
||||
|
||||
showCohortEditor: function(event) {
|
||||
onCohortSelected: function(event) {
|
||||
event.preventDefault();
|
||||
var selectedCohort = this.getSelectedCohort();
|
||||
this.lastSelectedCohortId = selectedCohort.get('id');
|
||||
this.showCohortEditor(selectedCohort);
|
||||
},
|
||||
|
||||
showCohortEditor: function(cohort) {
|
||||
this.removeNotification();
|
||||
if (this.editor) {
|
||||
this.editor.setCohort(selectedCohort);
|
||||
this.editor.setCohort(cohort);
|
||||
} else {
|
||||
this.editor = new CohortEditorView({
|
||||
el: this.$('.cohort-management-group'),
|
||||
model: selectedCohort,
|
||||
model: cohort,
|
||||
cohorts: this.model,
|
||||
advanced_settings_url: this.advanced_settings_url
|
||||
});
|
||||
this.editor.render();
|
||||
}
|
||||
},
|
||||
|
||||
showNotification: function(options, beforeElement) {
|
||||
var model = new NotificationModel(options);
|
||||
this.removeNotification();
|
||||
this.notification = new NotificationView({
|
||||
model: model
|
||||
});
|
||||
this.notification.render();
|
||||
if (!beforeElement) {
|
||||
beforeElement = this.$('.cohort-management-group');
|
||||
}
|
||||
beforeElement.before(this.notification.$el);
|
||||
},
|
||||
|
||||
removeNotification: function() {
|
||||
if (this.notification) {
|
||||
this.notification.remove();
|
||||
}
|
||||
},
|
||||
|
||||
showAddCohortForm: function(event) {
|
||||
event.preventDefault();
|
||||
this.removeNotification();
|
||||
this.addCohortForm = $(this.addCohortFormTemplate({}));
|
||||
this.addCohortForm.insertAfter(this.$('.cohort-management-nav'));
|
||||
this.setCohortEditorVisibility(false);
|
||||
},
|
||||
|
||||
hideAddCohortForm: function() {
|
||||
this.setCohortEditorVisibility(true);
|
||||
if (this.addCohortForm) {
|
||||
this.addCohortForm.remove();
|
||||
this.addCohortForm = null;
|
||||
}
|
||||
},
|
||||
|
||||
setCohortEditorVisibility: function(showEditor) {
|
||||
if (showEditor) {
|
||||
this.$('.cohort-management-group').removeClass(hiddenClass);
|
||||
this.$('.cohort-management-nav').removeClass(disabledClass);
|
||||
} else {
|
||||
this.$('.cohort-management-group').addClass(hiddenClass);
|
||||
this.$('.cohort-management-nav').addClass(disabledClass);
|
||||
}
|
||||
},
|
||||
|
||||
saveAddCohortForm: function(event) {
|
||||
event.preventDefault();
|
||||
var self = this,
|
||||
showAddError,
|
||||
cohortName = this.$('.cohort-create-name').val().trim();
|
||||
showAddError = function(message) {
|
||||
self.showNotification(
|
||||
{type: 'error', title: message},
|
||||
self.$('.cohort-management-create-form-name label')
|
||||
);
|
||||
};
|
||||
this.removeNotification();
|
||||
if (cohortName.length > 0) {
|
||||
$.post(
|
||||
this.model.url + '/add',
|
||||
{name: cohortName}
|
||||
).done(function(result) {
|
||||
if (result.success) {
|
||||
self.lastSelectedCohortId = result.cohort.id;
|
||||
self.model.fetch().done(function() {
|
||||
self.showNotification({
|
||||
type: 'confirmation',
|
||||
title: interpolate_text(
|
||||
gettext('The {cohortGroupName} cohort group has been created. You can manually add students to this group below.'),
|
||||
{cohortGroupName: cohortName}
|
||||
)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
showAddError(result.msg);
|
||||
}
|
||||
}).fail(function() {
|
||||
showAddError(gettext("We've encountered an error. Please refresh your browser and then try again."));
|
||||
});
|
||||
} else {
|
||||
showAddError(gettext('Please enter a name for your new cohort group.'));
|
||||
}
|
||||
},
|
||||
|
||||
cancelAddCohortForm: function(event) {
|
||||
event.preventDefault();
|
||||
this.removeNotification();
|
||||
this.onSync();
|
||||
}
|
||||
});
|
||||
|
||||
this.CohortsView = CohortsView;
|
||||
}).call(this, Backbone, CohortEditorView);
|
||||
}).call(this, $, _, Backbone, gettext, interpolate_text, CohortEditorView, NotificationModel, NotificationView);
|
||||
|
||||
@@ -9,17 +9,14 @@
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var actionText = null;
|
||||
// If no actionText is passed to the template, the action button
|
||||
// will not be shown.
|
||||
if (this.model.get("actionText") && this.model.get("actionCallback")) {
|
||||
actionText = this.model.get("actionText");
|
||||
}
|
||||
this.$el.html(this.template({
|
||||
type: this.model.get("type"),
|
||||
title: this.model.get("title"),
|
||||
message: this.model.get("message"),
|
||||
details: this.model.get("details"),
|
||||
actionText: actionText
|
||||
actionText: this.model.get("actionText"),
|
||||
actionClass: this.model.get("actionClass"),
|
||||
actionIconClass: this.model.get("actionIconClass")
|
||||
}));
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -454,8 +454,11 @@ section.instructor-dashboard-content-2 {
|
||||
|
||||
.form-submit {
|
||||
@include idashbutton($blue);
|
||||
@include font-size(14);
|
||||
@include line-height(14);
|
||||
margin-right: ($baseline/2);
|
||||
margin-bottom: 0;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.form-cancel {
|
||||
@@ -481,6 +484,21 @@ section.instructor-dashboard-content-2 {
|
||||
@include idashbutton($blue);
|
||||
float: right;
|
||||
text-align: right;
|
||||
text-shadow: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
// STATE: is disabled
|
||||
&.is-disabled {
|
||||
|
||||
|
||||
.cohort-select {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.action-create {
|
||||
opacity: 0.50;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user