Move display logic into view

Model shouldn't know about things like close buttons, visibility, and so on
This commit is contained in:
David Baumgold
2013-05-14 16:37:02 -04:00
parent 528987342a
commit 4af3325976
14 changed files with 204 additions and 125 deletions

View File

@@ -0,0 +1 @@
../../../templates/js/alert.underscore

View File

@@ -0,0 +1 @@
../../../templates/js/notification.underscore

View File

@@ -0,0 +1 @@
../../../templates/js/prompt.underscore

View File

@@ -1 +0,0 @@
../../../templates/js/system-feedback.underscore

View File

@@ -11,18 +11,6 @@ describe "CMS.Models.SystemFeedback", ->
it "should not have a type set by default", ->
expect(@model.get("type")).toBeNull()
it "should be shown by default", ->
expect(@model.get("shown")).toEqual(true)
it "should trigger a change event on calling .hide()", ->
spy = jasmine.createSpy()
@model.on("change", spy)
@model.hide()
expect(@model.get("shown")).toEqual(false)
expect(spy).toHaveBeenCalled()
describe "CMS.Models.WarningMessage", ->
beforeEach ->

View File

@@ -1,29 +1,23 @@
describe "CMS.Views.SystemFeedback", ->
tpl = readFixtures('system-feedback.underscore')
# CMS.Views.SystemFeedback looks for a template on the page when the code
# is loaded, and even if we set that template on the page using a fixture,
# CMS.Views.SystemFeedback has already been loaded, and so that template
# won't be picked up. This is a dirty hack, to load that template into
# the code after the code has been loaded already.
CMS.Views.SystemFeedback.prototype.template = _.template(tpl)
describe "CMS.Views.Alert as base class", ->
tpl = readFixtures('alert.underscore')
beforeEach ->
setFixtures(sandbox({id: "page-notification"}))
setFixtures(sandbox({id: "page-alert"}))
appendSetFixtures($("<script>", {id: "alert-tpl", type: "text/template"}).text(tpl))
@model = new CMS.Models.ConfirmationMessage({
"title": "Portal"
"message": "Welcome to the Aperture Science Computer-Aided Enrichment Center"
close: true
})
# it will be interesting to see when this.render is called, so lets spy on it
spyOn(CMS.Views.SystemFeedback.prototype, 'render').andCallThrough()
spyOn(CMS.Views.Alert.prototype, 'render').andCallThrough()
it "renders on initalize", ->
view = new CMS.Views.Notification({model: @model})
view = new CMS.Views.Alert({model: @model})
expect(view.render).toHaveBeenCalled()
it "renders the template", ->
view = new CMS.Views.Notification({model: @model})
view = new CMS.Views.Alert({model: @model})
expect(view.$(".action-close")).toBeDefined()
expect(view.$('.wrapper')).toHaveClass("is-shown")
text = view.$el.text()
@@ -31,26 +25,40 @@ describe "CMS.Views.SystemFeedback", ->
expect(text).toMatch(/Aperture Science/)
it "close button sends a .hide() message", ->
spyOn(CMS.Views.SystemFeedback.prototype, 'hide').andCallThrough()
spyOn(CMS.Views.Alert.prototype, 'hide').andCallThrough()
view = new CMS.Views.Notification({model: @model})
view = new CMS.Views.Alert({model: @model})
view.$(".action-close").click()
expect(CMS.Views.SystemFeedback.prototype.hide).toHaveBeenCalled()
expect(CMS.Views.Alert.prototype.hide).toHaveBeenCalled()
expect(view.$('.wrapper')).not.toHaveClass("is-shown")
expect(view.$('.wrapper')).toHaveClass("is-hiding")
describe "CMS.Views.Notification", ->
tpl = readFixtures('notification.underscore')
beforeEach ->
setFixtures(sandbox({id: "page-notification"}))
appendSetFixtures($("<script>", {id: "notification-tpl", type: "text/template"}).text(tpl))
@model = new CMS.Models.ConfirmationMessage({
"title": "Portal"
"message": "Welcome to the Aperture Science Computer-Aided Enrichment Center"
})
# for some reason, expect($("body")) blows up the test runner, so this test
# just exercises the Prompt rather than asserting on anything. Best I can
# do for now. :(
it "prompt view changes class on body", ->
it "changes class on body", ->
# expect($("body")).not.toHaveClass("prompt-is-shown")
view = new CMS.Views.Prompt({model: @model})
# expect($("body")).toHaveClass("prompt-is-shown")
@model.hide()
view.hide()
# expect($("body")).not.toHaveClass("prompt-is-shown")
describe "SystemFeedback click events", ->
describe "CMS.Views.Alert click events", ->
tpl = readFixtures('alert.underscore')
beforeEach ->
@model = new CMS.Models.WarningMessage(
title: "Unsaved",
@@ -69,6 +77,7 @@ describe "SystemFeedback click events", ->
)
setFixtures(sandbox({id: "page-alert"}))
appendSetFixtures($("<script>", {id: "alert-tpl", type: "text/template"}).text(tpl))
@view = new CMS.Views.Alert({model: @model})
it "should trigger the primary event on a primary click", ->

View File

@@ -2,11 +2,7 @@ CMS.Models.SystemFeedback = Backbone.Model.extend({
defaults: {
"type": null, // "warning", "confirmation", "error", "announcement", "step-required", etc
"title": "",
"message": "",
"shown": true,
"close": false, // show a close button?
"icon": true, // show an icon?
"status": false // example: "saving" popup
"message": ""
/* could also have an "actions" hash: here is an example demonstrating
the expected structure
"actions": {
@@ -31,12 +27,6 @@ CMS.Models.SystemFeedback = Backbone.Model.extend({
]
}
*/
},
show: function() {
this.set("shown", true);
},
hide: function() {
this.set("shown", false);
}
});

View File

@@ -25,18 +25,19 @@ CMS.Models.Section = Backbone.Model.extend({
if(!this.msg) {
this.msg = new CMS.Models.SystemFeedback({
type: "saving",
title: "Saving&hellip;",
icon: true,
status: true
title: "Saving&hellip;"
});
}
if(!this.msgView) {
this.msgView = new CMS.Views.Notification({model: this.msg});
this.msgView = new CMS.Views.Notification({
model: this.msg,
closeIcon: false
});
}
this.msg.show();
this.msgView.show();
},
hideNotification: function() {
if(!this.msg) { return; }
this.msg.hide();
if(!this.msgView) { return; }
this.msgView.hide();
}
});

View File

@@ -1,17 +1,17 @@
CMS.Views.SystemFeedback = Backbone.View.extend({
CMS.Views.Alert = Backbone.View.extend({
options: {
type: "alert",
shown: true, // is this view currently being shown?
closeIcon: true // should we render a close button in the top right corner?
},
initialize: function() {
this.setElement($("#page-"+this.type));
this.template = _.template($("#"+this.options.type+"-tpl").text()),
this.setElement($("#page-"+this.options.type));
this.listenTo(this.model, 'change', this.render);
return this.render();
},
template: _.template($("#system-feedback-tpl").text()),
render: function() {
var attrs = $.extend({}, this.model.attributes);
if(attrs.type) {
attrs.modelType = attrs.type;
delete attrs.type;
}
attrs.viewType = this.type;
var attrs = $.extend({}, this.options, this.model.attributes);
this.$el.html(this.template(attrs));
return this;
},
@@ -20,8 +20,13 @@ CMS.Views.SystemFeedback = Backbone.View.extend({
"click .action-primary": "primaryClick",
"click .action-secondary": "secondaryClick"
},
show: function() {
this.options.shown = true;
return this.render();
},
hide: function() {
this.model.hide();
this.options.shown = false;
return this.render();
},
primaryClick: function() {
var actions = this.model.get("actions");
@@ -29,7 +34,7 @@ CMS.Views.SystemFeedback = Backbone.View.extend({
var primary = actions.primary;
if(!primary) { return; }
if(primary.click) {
primary.click.call(this.model);
primary.click.call(this.model, this);
}
},
secondaryClick: function(e) {
@@ -46,19 +51,22 @@ CMS.Views.SystemFeedback = Backbone.View.extend({
}
var secondary = this.model.get("actions").secondary[i];
if(secondary.click) {
secondary.click.call(this.model);
secondary.click.call(this.model, this);
}
}
});
CMS.Views.Alert = CMS.Views.SystemFeedback.extend({
type: "alert"
CMS.Views.Notification = CMS.Views.Alert.extend({
options: $.extend({}, CMS.Views.Alert.prototype.options, {
type: "notification",
closeIcon: false
})
});
CMS.Views.Notification = CMS.Views.SystemFeedback.extend({
type: "notification"
});
CMS.Views.Prompt = CMS.Views.SystemFeedback.extend({
type: "prompt",
CMS.Views.Prompt = CMS.Views.Alert.extend({
options: $.extend({}, CMS.Views.Alert.prototype.options, {
type: "prompt",
closeIcon: false
}),
render: function() {
if(!window.$body) { window.$body = $(document.body); }
if(this.model.get('shown')) {
@@ -67,6 +75,6 @@ CMS.Views.Prompt = CMS.Views.SystemFeedback.extend({
$body.removeClass('prompt-is-shown');
}
// super() in Javascript has awkward syntax :(
return CMS.Views.SystemFeedback.prototype.render.apply(this, arguments);
return CMS.Views.Alert.prototype.render.apply(this, arguments);
}
});