├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── messages.js ├── messages_list.html ├── messages_list.js ├── messages_tests.js ├── package.js └── versions.json /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | flash-messages 3 | smart.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_install: 5 | - "curl -L http://git.io/ejPSng | /bin/sh" 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Juan Camilo Mejia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | flash-messages [![Build Status](https://travis-ci.org/camilosw/flash-messages.svg)](https://travis-ci.org/camilosw/flash-messages) 2 | ============== 3 | 4 | 5 | Package for displaying flash messages to the user. This is based on the chapter 'Creating a Meteorite Package' from the [Discover Meteor Book](http://www.discovermeteor.com/) and the [foundation-flash-messages](https://github.com/datariot/foundation-flash-messages) package. 6 | 7 | This package integrates well with [Bootstrap Alerts](http://getbootstrap.com/components/#alerts) styles, but Bootstrap is not a dependency. 8 | 9 | You can see a [demo](http://flash-messages-demo.meteor.com/) and their [source code](https://github.com/camilosw/flash-messages-demo). 10 | 11 | ## Note 12 | 13 | The syntax has changed on version 0.2.0 14 | 15 | ## Usage 16 | 17 | Include the template somewhere in your index.html file: 18 | ```javascript 19 | {{> flashMessages}} 20 | ``` 21 | And then send messages: 22 | ```javascript 23 | FlashMessages.sendWarning("Message"); 24 | FlashMessages.sendError("Message"); 25 | FlashMessages.sendSuccess("Message"); 26 | FlashMessages.sendInfo("Message"); 27 | ``` 28 | 29 | **Note:** sendAlert was deprecated, use sendWarning instead. 30 | 31 | You can also send a group of messages sending an array of strings. This will be rendered on a `ul` `li` list: 32 | ```javascript 33 | FlashMessages.sendInfo(["Message 1", "Message 2", "Message 3"]); 34 | ``` 35 | 36 | Messages can also contain html: 37 | ```javascript 38 | FlashMessages.sendInfo("You can found Meteor here"); 39 | ``` 40 | 41 | To clear messages: 42 | ```javascript 43 | FlashMessages.clear(); 44 | ``` 45 | 46 | Only the seen messages will be cleared. 47 | 48 | ## Configure 49 | 50 | You can configure globally the way the messages behave with FlashMessages.configure (the below sample shows the default values): 51 | ```javascript 52 | FlashMessages.configure({ 53 | autoHide: true, 54 | hideDelay: 5000, 55 | autoScroll: true 56 | }); 57 | ``` 58 | 59 | - `autoHide`: set to `true` to make flash message fade after `hideDelay` milliseconds, set to `false` to require the user to click the close button on the message to dismiss it. 60 | - `hideDelay`: set the desired number of milliseconds for the flash message to be displayed (when `autoHide` is `true`). 61 | - `autoScroll`: set to `true` to enable auto scroll when a message is displayed, `false` to disable auto scroll. (**Note:** this can be set only globally.) 62 | 63 | You can also set individual options on messages. This will override global configuration: 64 | ```javascript 65 | FlashMessages.sendWarning("Message", { autoHide: false }); 66 | FlashMessages.sendError("Message", { hideDelay: 2000 }); 67 | FlashMessages.sendSuccess("Message", { autoHide: true, hideDelay: 8000 }); 68 | ``` 69 | -------------------------------------------------------------------------------- /messages.js: -------------------------------------------------------------------------------- 1 | /** 2 | * flashMessages 3 | * { message: String, 4 | * style: String, 5 | * seen: Boolean } 6 | */ 7 | flashMessages = new Mongo.Collection(null); 8 | 9 | FlashMessages = { 10 | // Deprecated, use sendWarning instead. sendWarning is more consistent with Boostrap classes. 11 | sendAlert: function(message, options) { 12 | sendMessage(message, '', options); 13 | console.log('Deprecated, use sendWarning instead of sendAlert'); 14 | }, 15 | sendWarning: function(message, options) { 16 | sendMessage(message, 'alert-warning', options); 17 | }, 18 | sendError: function(message, options) { 19 | sendMessage(message, 'alert-error alert-danger', options); 20 | }, 21 | sendSuccess: function(message, options) { 22 | sendMessage(message, 'alert-success', options); 23 | }, 24 | sendInfo: function(message, options) { 25 | sendMessage(message, 'alert-info', options); 26 | }, 27 | clear: function() { 28 | flashMessages.remove({seen: true}); 29 | }, 30 | configure: function(options) { 31 | this.options = this.options || {}; 32 | _.extend(this.options, options); 33 | }, 34 | options: { 35 | autoHide: true, 36 | hideDelay: 5000, 37 | autoScroll: true 38 | } 39 | } 40 | 41 | sendMessage = function(message, style, options) { 42 | options = options || {}; 43 | options.autoHide = options.autoHide === undefined ? FlashMessages.options.autoHide : options.autoHide; 44 | options.hideDelay = options.hideDelay || FlashMessages.options.hideDelay; 45 | flashMessages.insert({ message: message, style: style, seen: false, options: options}); 46 | } -------------------------------------------------------------------------------- /messages_list.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /messages_list.js: -------------------------------------------------------------------------------- 1 | Template.flashMessages.helpers({ 2 | messages: function () { 3 | if (flashMessages.find().count() && FlashMessages.options.autoScroll) 4 | $('html, body').animate({ 5 | scrollTop: 0 6 | }, 200); 7 | var messages = flashMessages.find().fetch(); 8 | $.each(messages, function(index, value) { 9 | value.group = value.message instanceof Array; 10 | }); 11 | return messages; 12 | } 13 | }); 14 | 15 | Template.flashMessageItem.rendered = function () { 16 | var message = this.data; 17 | Meteor.defer(function() { 18 | flashMessages.update(message._id, {$set: {seen: true}}); 19 | }); 20 | if (message.options && message.options.autoHide) { 21 | var $alert = $(this.find('.alert')); 22 | Meteor.setTimeout(function() { 23 | $alert.fadeOut(400, function() { 24 | flashMessages.remove({_id: message._id}); 25 | }); 26 | }, 27 | message.options.hideDelay); 28 | } 29 | }; 30 | 31 | Template.flashMessageItem.events({ 32 | "click .close": function (e, tmpl) { 33 | e.preventDefault(); 34 | flashMessages.remove(tmpl.data._id); 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /messages_tests.js: -------------------------------------------------------------------------------- 1 | // Helpers 2 | 3 | var messagesCount = function() { 4 | return flashMessages.find({}).count(); 5 | } 6 | 7 | var findOneMessage = function() { 8 | return flashMessages.findOne({}); 9 | } 10 | 11 | var cleanMessages = function() { 12 | flashMessages.remove({}); 13 | } 14 | 15 | // Tests 16 | 17 | Tinytest.add('flash-messages - Messages Collection works', function(test) { 18 | test.equal(messagesCount(), 0); 19 | }); 20 | 21 | Tinytest.add('flash-messages - Add warning message', function(test) { 22 | cleanMessages(); 23 | var message = 'This is a warning message'; 24 | FlashMessages.sendWarning(message); 25 | 26 | test.equal(messagesCount(), 1); 27 | 28 | test.equal(findOneMessage().message, message, 29 | 'Warning messages should be ' + message); 30 | 31 | test.equal(findOneMessage().style, 'alert-warning', 'Style should be alert-warning'); 32 | 33 | test.equal(findOneMessage().seen, false, 'Seen should be false'); 34 | }); 35 | 36 | Tinytest.add('flash-messages - Add error message', function(test) { 37 | cleanMessages(); 38 | var message = 'This is an error message'; 39 | FlashMessages.sendError(message); 40 | 41 | test.equal(messagesCount(), 1); 42 | 43 | test.equal(findOneMessage().message, message, 44 | 'Error messages should be ' + message); 45 | 46 | test.equal(findOneMessage().style, 'alert-error alert-danger', 47 | 'Style should be alert-error'); 48 | 49 | test.equal(findOneMessage().seen, false, 'Seen should be false'); 50 | }); 51 | 52 | Tinytest.add('flash-messages - Add success message', function(test) { 53 | cleanMessages(); 54 | var message = 'This is a success message'; 55 | FlashMessages.sendSuccess(message); 56 | 57 | test.equal(messagesCount(), 1); 58 | 59 | test.equal(findOneMessage().message, message, 60 | 'Success messages should be ' + message); 61 | 62 | test.equal(findOneMessage().style, 'alert-success', 'Style should be alert-success'); 63 | 64 | test.equal(findOneMessage().seen, false, 'Seen should be false'); 65 | }); 66 | 67 | Tinytest.add('flash-messages - Add info message', function(test) { 68 | cleanMessages(); 69 | var message = 'This is an info message'; 70 | FlashMessages.sendInfo(message); 71 | 72 | test.equal(messagesCount(), 1); 73 | 74 | test.equal(findOneMessage().message, message, 75 | 'Info messages should be ' + message); 76 | 77 | test.equal(findOneMessage().style, 'alert-info', 'Style should be alert-info'); 78 | 79 | test.equal(findOneMessage().seen, false, 'Seen should be false'); 80 | }); 81 | 82 | Tinytest.add('flash-messages - Add messages grouped', function(test) { 83 | cleanMessages(); 84 | var messages = ['Message 1', 'Message 2', 'Message 3']; 85 | FlashMessages.sendInfo(messages); 86 | 87 | test.equal(messagesCount(), 1); 88 | }); 89 | 90 | Tinytest.add("flash-messages - Don't remove unseen messages", function(test) { 91 | cleanMessages(); 92 | FlashMessages.sendError('message'); 93 | FlashMessages.clear(); 94 | test.equal(messagesCount(), 1); 95 | }); 96 | 97 | testAsyncMulti('flash-messages - Remove seen messages', [ 98 | function(test, expect) { 99 | cleanMessages(); 100 | FlashMessages.sendError('message'); 101 | 102 | UI.insert(UI.render(Template.flashMessages), document.body); 103 | Meteor.setTimeout(expect(function(){ 104 | test.equal(messagesCount(), 1); 105 | test.equal(flashMessages.find({seen: false}).count(), 0, 106 | 'Messages should be marqued as seen (seen: true)'); 107 | FlashMessages.clear(); 108 | test.equal(flashMessages.find({seen: true}).count(), 0, 109 | 'Messages seen should be cleared'); 110 | }), 500); 111 | } 112 | ]); 113 | 114 | testAsyncMulti('flash-messages - Remove when click close button', [ 115 | function(test, expect) { 116 | cleanMessages(); 117 | FlashMessages.sendError('message'); 118 | 119 | UI.insert(UI.render(Template.flashMessages), document.body); 120 | Meteor.setTimeout(expect(function(){ 121 | test.equal(messagesCount(), 1); 122 | clickElement(document.getElementsByClassName('close')[0]); 123 | test.equal(messagesCount(), 0); 124 | }), 500); 125 | } 126 | ]); 127 | 128 | testAsyncMulti('flash-messages - Remove after 5 seconds', [ 129 | function(test, expect) { 130 | cleanMessages(); 131 | FlashMessages.sendError('message'); 132 | 133 | UI.insert(UI.render(Template.flashMessages), document.body); 134 | Meteor.setTimeout(expect(function(){ 135 | test.equal(messagesCount(), 1); 136 | }), 500); 137 | Meteor.setTimeout(expect(function(){ 138 | test.equal(messagesCount(), 0); 139 | }), 6000); 140 | } 141 | ]); 142 | 143 | testAsyncMulti("flash-messages - Don't remove if autoHide is false", [ 144 | function(test, expect) { 145 | cleanMessages(); 146 | FlashMessages.sendError('message', { autoHide: false }); 147 | 148 | UI.insert(UI.render(Template.flashMessages), document.body); 149 | Meteor.setTimeout(expect(function(){ 150 | test.equal(messagesCount(), 1); 151 | }), 500); 152 | Meteor.setTimeout(expect(function(){ 153 | test.equal(messagesCount(), 1); 154 | }), 6000); 155 | } 156 | ]); 157 | 158 | testAsyncMulti("flash-messages - Don't remove with global config", [ 159 | function(test, expect) { 160 | cleanMessages(); 161 | var options = _.clone(FlashMessages.options); 162 | FlashMessages.configure({ autoHide: false }); 163 | FlashMessages.sendError('message'); 164 | FlashMessages.options = options 165 | 166 | UI.insert(UI.render(Template.flashMessages), document.body); 167 | Meteor.setTimeout(expect(function(){ 168 | test.equal(messagesCount(), 1); 169 | }), 500); 170 | Meteor.setTimeout(expect(function(){ 171 | test.equal(messagesCount(), 1); 172 | }), 6000); 173 | } 174 | ]); 175 | 176 | testAsyncMulti('flash-messages - Set auto hide delay to 1 second', [ 177 | function(test, expect) { 178 | cleanMessages(); 179 | FlashMessages.sendError('message', { hideDelay: 1000 }); 180 | 181 | UI.insert(UI.render(Template.flashMessages), document.body); 182 | Meteor.setTimeout(expect(function(){ 183 | test.equal(messagesCount(), 1); 184 | }), 500); 185 | Meteor.setTimeout(expect(function(){ 186 | test.equal(messagesCount(), 0); 187 | }), 2000); 188 | } 189 | ]); 190 | 191 | testAsyncMulti('flash-messages - Set auto hide delay to 1 second with global config', [ 192 | function(test, expect) { 193 | cleanMessages(); 194 | var options = _.clone(FlashMessages.options); 195 | FlashMessages.configure({ hideDelay: 1000 }); 196 | FlashMessages.sendError('message'); 197 | FlashMessages.options = options 198 | 199 | UI.insert(UI.render(Template.flashMessages), document.body); 200 | Meteor.setTimeout(expect(function(){ 201 | test.equal(messagesCount(), 1); 202 | }), 500); 203 | Meteor.setTimeout(expect(function(){ 204 | test.equal(messagesCount(), 0); 205 | }), 2000); 206 | } 207 | ]); 208 | 209 | testAsyncMulti('flash-messages - Override global config', [ 210 | function(test, expect) { 211 | cleanMessages(); 212 | var options = _.clone(FlashMessages.options); 213 | FlashMessages.configure({ autoHide: false, hideDelay: 8000 }); 214 | FlashMessages.sendError('message', { autoHide: true, hideDelay: 1000 }); 215 | FlashMessages.options = options 216 | 217 | UI.insert(UI.render(Template.flashMessages), document.body); 218 | Meteor.setTimeout(expect(function(){ 219 | test.equal(messagesCount(), 1); 220 | }), 500); 221 | Meteor.setTimeout(expect(function(){ 222 | test.equal(messagesCount(), 0); 223 | }), 2000); 224 | } 225 | ]); -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'mrt:flash-messages', 3 | summary: 'A package to display flash messages to the user', 4 | version: '1.0.1', 5 | git: 'https://github.com/camilosw/flash-messages.git' 6 | }); 7 | 8 | Package.onUse(function(api) { 9 | api.versionsFrom('0.9.0'); 10 | api.use([ 11 | 'minimongo', 12 | 'mongo-livedata', 13 | 'templating', 14 | 'underscore', 15 | ], 'client'); 16 | api.addFiles(['messages.js', 'messages_list.html', 'messages_list.js'], 'client'); 17 | 18 | if (api.export) { 19 | api.export(['FlashMessages', 'flashMessages'], 'client'); 20 | } 21 | }); 22 | 23 | Package.on_test(function(api) { 24 | api.use('mrt:flash-messages', 'client'); 25 | api.use(['tinytest', 'test-helpers'], 'client'); 26 | 27 | api.addFiles('messages_tests.js', 'client'); 28 | }); 29 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | [ 4 | "application-configuration", 5 | "1.0.3" 6 | ], 7 | [ 8 | "base64", 9 | "1.0.1" 10 | ], 11 | [ 12 | "binary-heap", 13 | "1.0.1" 14 | ], 15 | [ 16 | "blaze", 17 | "2.0.3" 18 | ], 19 | [ 20 | "callback-hook", 21 | "1.0.1" 22 | ], 23 | [ 24 | "check", 25 | "1.0.2" 26 | ], 27 | [ 28 | "ddp", 29 | "1.0.11" 30 | ], 31 | [ 32 | "deps", 33 | "1.0.5" 34 | ], 35 | [ 36 | "ejson", 37 | "1.0.4" 38 | ], 39 | [ 40 | "follower-livedata", 41 | "1.0.2" 42 | ], 43 | [ 44 | "geojson-utils", 45 | "1.0.1" 46 | ], 47 | [ 48 | "htmljs", 49 | "1.0.2" 50 | ], 51 | [ 52 | "id-map", 53 | "1.0.1" 54 | ], 55 | [ 56 | "jquery", 57 | "1.0.1" 58 | ], 59 | [ 60 | "json", 61 | "1.0.1" 62 | ], 63 | [ 64 | "logging", 65 | "1.0.5" 66 | ], 67 | [ 68 | "meteor", 69 | "1.1.3" 70 | ], 71 | [ 72 | "minimongo", 73 | "1.0.5" 74 | ], 75 | [ 76 | "mongo", 77 | "1.0.8" 78 | ], 79 | [ 80 | "mongo-livedata", 81 | "1.0.6" 82 | ], 83 | [ 84 | "observe-sequence", 85 | "1.0.3" 86 | ], 87 | [ 88 | "ordered-dict", 89 | "1.0.1" 90 | ], 91 | [ 92 | "random", 93 | "1.0.1" 94 | ], 95 | [ 96 | "reactive-var", 97 | "1.0.3" 98 | ], 99 | [ 100 | "retry", 101 | "1.0.1" 102 | ], 103 | [ 104 | "templating", 105 | "1.0.9" 106 | ], 107 | [ 108 | "tracker", 109 | "1.0.3" 110 | ], 111 | [ 112 | "underscore", 113 | "1.0.1" 114 | ] 115 | ], 116 | "pluginDependencies": [], 117 | "toolVersion": "meteor-tool@1.0.35", 118 | "format": "1.0" 119 | } --------------------------------------------------------------------------------