Move display logic into view
Model shouldn't know about things like close buttons, visibility, and so on
This commit is contained in:
1
cms/static/coffee/fixtures/alert.underscore
Symbolic link
1
cms/static/coffee/fixtures/alert.underscore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../templates/js/alert.underscore
|
||||
1
cms/static/coffee/fixtures/notification.underscore
Symbolic link
1
cms/static/coffee/fixtures/notification.underscore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../templates/js/notification.underscore
|
||||
1
cms/static/coffee/fixtures/prompt.underscore
Symbolic link
1
cms/static/coffee/fixtures/prompt.underscore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../templates/js/prompt.underscore
|
||||
@@ -1 +0,0 @@
|
||||
../../../templates/js/system-feedback.underscore
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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", ->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -25,18 +25,19 @@ CMS.Models.Section = Backbone.Model.extend({
|
||||
if(!this.msg) {
|
||||
this.msg = new CMS.Models.SystemFeedback({
|
||||
type: "saving",
|
||||
title: "Saving…",
|
||||
icon: true,
|
||||
status: true
|
||||
title: "Saving…"
|
||||
});
|
||||
}
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -32,8 +32,14 @@
|
||||
<%include file="courseware_vendor_js.html"/>
|
||||
|
||||
## js templates
|
||||
<script id="system-feedback-tpl" type="text/template">
|
||||
<%static:include path="js/system-feedback.underscore" />
|
||||
<script id="alert-tpl" type="text/template">
|
||||
<%static:include path="js/alert.underscore" />
|
||||
</script>
|
||||
<script id="notification-tpl" type="text/template">
|
||||
<%static:include path="js/notification.underscore" />
|
||||
</script>
|
||||
<script id="prompt-tpl" type="text/template">
|
||||
<%static:include path="js/prompt.underscore" />
|
||||
</script>
|
||||
|
||||
## javascript
|
||||
@@ -68,8 +74,8 @@ $(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {
|
||||
"actions": {
|
||||
"primary": {
|
||||
"text": "Dismiss",
|
||||
"click": function() {
|
||||
this.hide();
|
||||
"click": function(view) {
|
||||
view.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
cms/templates/js/alert.underscore
Normal file
45
cms/templates/js/alert.underscore
Normal file
@@ -0,0 +1,45 @@
|
||||
<div class="wrapper wrapper-alert wrapper-alert-<%= type %>
|
||||
<% if(obj.shown) { %>is-shown<% } else { %>is-hiding<% } %>
|
||||
id="alert-<%= type %>"
|
||||
aria-hidden="<% if(obj.shown) { %>false<% } else { %>true<% } %>"
|
||||
aria-labelledby="alert-<%= type %>-title"
|
||||
<% if (obj.message) { %>aria-describedby="alert-<%= type %>-description" <% } %>
|
||||
<% if (obj.actions) { %>role="dialog"<% } %>
|
||||
>
|
||||
<div class="alert <%= type %> <% if(obj.actions) { %>has-actions<% } %>">
|
||||
<% var iconText = {"warning": "⚠", "confirmation": "✓", "error": "⚠", "announcement": "📢", "step-required": "", "help": "❓", "saving": "⚙"} %>
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-<%= type %>"><%= iconText[type] %></i>
|
||||
|
||||
<div class="copy">
|
||||
<h2 class="title title-3" id="alert-<%= type %>-title"><%= title %></h2>
|
||||
<% if(obj.message) { %><p class="message" id="alert-<%= type %>-description"><%= message %></p><% } %>
|
||||
</div>
|
||||
|
||||
<% if(obj.actions) { %>
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Alert Actions</h3>
|
||||
<ul>
|
||||
<% if(actions.primary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-primary <%= actions.primary.class %>"><%= actions.primary.text %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if(actions.secondary) {
|
||||
_.each(actions.secondary, function(secondary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-secondary <%= secondary.class %>"><%= secondary.text %></a>
|
||||
</li>
|
||||
<% });
|
||||
} %>
|
||||
</ul>
|
||||
</nav>
|
||||
<% } %>
|
||||
|
||||
<% if(obj.closeIcon) { %>
|
||||
<a href="#" rel="view" class="action action-close action-alert-close">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-close">␡</i>
|
||||
<span class="label">close alert</span>
|
||||
</a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
46
cms/templates/js/notification.underscore
Normal file
46
cms/templates/js/notification.underscore
Normal file
@@ -0,0 +1,46 @@
|
||||
<div class="wrapper wrapper-notification wrapper-notification-<%= type %>
|
||||
<% if(obj.shown) { %>is-shown<% } else { %>is-hiding<% } %>
|
||||
<% if(_.contains(['help', 'saving'], type)) { %>wrapper-notification-status<% } %>"
|
||||
id="notification-<%= type %>"
|
||||
aria-hidden="<% if(obj.shown) { %>false<% } else { %>true<% } %>"
|
||||
aria-labelledby="notification-<%= type %>-title"
|
||||
<% if (obj.message) { %>aria-describedby="notification-<%= type %>-description" <% } %>
|
||||
<% if (obj.actions) { %>role="dialog"<% } %>
|
||||
>
|
||||
<div class="notification <%= type %> <% if(obj.actions) { %>has-actions<% } %>">
|
||||
<% var iconText = {"warning": "⚠", "confirmation": "✓", "error": "⚠", "announcement": "📢", "step-required": "", "help": "❓", "saving": "⚙"} %>
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-<%= type %>"><%= iconText[type] %></i>
|
||||
|
||||
<div class="copy">
|
||||
<h2 class="title title-3" id="notification-<%= type %>-title"><%= title %></h2>
|
||||
<% if(obj.message) { %><p class="message" id="notification-<%= type %>-description"><%= message %></p><% } %>
|
||||
</div>
|
||||
|
||||
<% if(obj.actions) { %>
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Notification Actions</h3>
|
||||
<ul>
|
||||
<% if(actions.primary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-primary <%= actions.primary.class %>"><%= actions.primary.text %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if(actions.secondary) {
|
||||
_.each(actions.secondary, function(secondary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-secondary <%= secondary.class %>"><%= secondary.text %></a>
|
||||
</li>
|
||||
<% });
|
||||
} %>
|
||||
</ul>
|
||||
</nav>
|
||||
<% } %>
|
||||
|
||||
<% if(obj.closeIcon) { %>
|
||||
<a href="#" rel="view" class="action action-close action-notification-close">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-close">␡</i>
|
||||
<span class="label">close notification</span>
|
||||
</a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
35
cms/templates/js/prompt.underscore
Normal file
35
cms/templates/js/prompt.underscore
Normal file
@@ -0,0 +1,35 @@
|
||||
<div class="wrapper wrapper-prompt wrapper-prompt-<%= type %>
|
||||
<% if(obj.shown) { %>is-shown<% } else { %>is-hiding<% } %>
|
||||
id="prompt-<%= type %>"
|
||||
aria-hidden="<% if(obj.shown) { %>false<% } else { %>true<% } %>"
|
||||
aria-labelledby="prompt-<%= type %>-title"
|
||||
<% if (obj.message) { %>aria-describedby="prompt-<%= type %>-description" <% } %>
|
||||
<% if (obj.actions) { %>role="dialog"<% } %>
|
||||
>
|
||||
<div class="prompt <%= type %> <% if(obj.actions) { %>has-actions<% } %>">
|
||||
<div class="copy">
|
||||
<h2 class="title title-3" id="prompt-<%= type %>-title"><%= title %></h2>
|
||||
<% if(obj.message) { %><p class="message" id="prompt-<%= type %>-description"><%= message %></p><% } %>
|
||||
</div>
|
||||
|
||||
<% if(obj.actions) { %>
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Prompt Actions</h3>
|
||||
<ul>
|
||||
<% if(actions.primary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-primary <%= actions.primary.class %>"><%= actions.primary.text %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if(actions.secondary) {
|
||||
_.each(actions.secondary, function(secondary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-secondary <%= secondary.class %>"><%= secondary.text %></a>
|
||||
</li>
|
||||
<% });
|
||||
} %>
|
||||
</ul>
|
||||
</nav>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,51 +0,0 @@
|
||||
<div class="wrapper
|
||||
wrapper-<%= viewType %>
|
||||
wrapper-<%= viewType %>-<%= modelType %>
|
||||
<% if(obj.status) { %>wrapper-<%= viewType %>-status <% } %>
|
||||
<% if(obj.shown) { %>is-shown<% } else { %>is-hiding<% } %>
|
||||
<% if(obj.fleeting) { %>is-fleeting<% } %>"
|
||||
id="<%= viewType %>-<%= modelType %>"
|
||||
aria-hidden="<% if(obj.shown) { %>false<% } else { %>true<% } %>"
|
||||
aria-labelledby="<%= viewType %>-<%= modelType %>-title"
|
||||
<% if (obj.message) { %>aria-describedby="<%= viewType %>-<%= modelType %>-description" <% } %>
|
||||
<% if (obj.actions) { %>role="dialog"<% } %>
|
||||
>
|
||||
<div class="<%= modelType %> <%= viewType %> <% if(obj.actions) { %>has-actions <% } %>">
|
||||
<% if (icon) { %>
|
||||
<% var iconText = {"warning": "⚠", "confirmation": "✓", "error": "⚠", "announcement": "📢", "step-required": "", "help": "❓", "saving": "⚙"} %>
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-<%= modelType %>"><%= iconText[modelType] %></i>
|
||||
<% } %>
|
||||
|
||||
<div class="copy">
|
||||
<h2 class="title title-3" id="<%= viewType %>-<%= modelType %>-title"><%= title %></h2>
|
||||
<% if(obj.message) { %><p class="message" id="<%= viewType %>-<%= modelType %>-description"><%= message %></p><% } %>
|
||||
</div>
|
||||
|
||||
<% if(obj.actions) { %>
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr"><%= viewType %> Actions</h3>
|
||||
<ul>
|
||||
<% if(actions.primary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-primary <%= actions.primary.class %>"><%= actions.primary.text %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if(actions.secondary) {
|
||||
_.each(actions.secondary, function(secondary) { %>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="button action-secondary <%= secondary.class %>"><%= secondary.text %></a>
|
||||
</li>
|
||||
<% });
|
||||
} %>
|
||||
</ul>
|
||||
</nav>
|
||||
<% } %>
|
||||
|
||||
<% if(obj.close) { %>
|
||||
<a href="#" rel="view" class="action action-close action-<%= viewType %>-close">
|
||||
<i class="ss-icon ss-symbolicons-block icon icon-close">␡</i>
|
||||
<span class="label">close <%= viewType %></span>
|
||||
</a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user