Skip to content

Commit

Permalink
Merge pull request #7916 from elastic/jasper/backport/7781/4.x
Browse files Browse the repository at this point in the history
[backport] PR #7781 to 4.x
  • Loading branch information
w33ble authored Aug 9, 2016
2 parents d4a0308 + f42ca57 commit e4eb59b
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 9 deletions.
129 changes: 126 additions & 3 deletions src/ui/public/notify/__tests__/notifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@ describe('Notifier', function () {
let _ = require('lodash');
let ngMock = require('ngMock');
let expect = require('expect.js');
let sinon = require('sinon');
let Notifier = require('ui/notify/notifier');

let message = 'Oh, the humanity!';
let notifier;
let params;
let version = window.__KBN__.version;
let buildNum = window.__KBN__.buildNum;
const version = window.__KBN__.version;
const buildNum = window.__KBN__.buildNum;
const message = 'Oh, the humanity!';
const customText = 'fooMarkup';
const customParams = {
title: 'fooTitle',
actions:[{
text: 'Cancel',
callback: sinon.spy()
}, {
text: 'OK',
callback: sinon.spy()
}]
};

beforeEach(ngMock.module('kibana'));

Expand Down Expand Up @@ -138,6 +150,117 @@ describe('Notifier', function () {
});
});

describe('#custom', function () {
let customNotification;

beforeEach(() => {
customNotification = notifier.custom(customText, customParams);
});

afterEach(() => {
customNotification.clear();
});

it('throws if second param is not an object', function () {
// destroy the default custom notification, avoid duplicate handling
customNotification.clear();

function callCustomIncorrectly() {
const badParam = null;
customNotification = notifier.custom(customText, badParam);
}
expect(callCustomIncorrectly).to.throwException(function (e) {
expect(e.message).to.be('config param is required, and must be an object');
});

});

it('has a custom function to make notifications', function () {
expect(notifier.custom).to.be.a('function');
});

it('properly merges options', function () {
// destroy the default custom notification, avoid duplicate handling
customNotification.clear();

const explicitLifetimeParams = _.defaults({ lifetime: 20000 }, customParams);
customNotification = notifier.custom(customText, explicitLifetimeParams);

expect(customNotification).to.have.property('type', 'info'); // default
expect(customNotification).to.have.property('title', explicitLifetimeParams.title); // passed in
expect(customNotification).to.have.property('lifetime', explicitLifetimeParams.lifetime); // passed in

expect(explicitLifetimeParams.type).to.be(undefined);
expect(explicitLifetimeParams.title).to.be.a('string');
expect(explicitLifetimeParams.lifetime).to.be.a('number');
});

it('sets the content', function () {
expect(customNotification).to.have.property('content', `${params.location}: ${customText}`);
expect(customNotification.content).to.be.a('string');
});

it('uses custom actions', function () {
expect(customNotification).to.have.property('customActions');
expect(customNotification.customActions).to.have.length(customParams.actions.length);
});

it('gives a default action if none are provided', function () {
// destroy the default custom notification, avoid duplicate handling
customNotification.clear();

const noActionParams = _.defaults({ actions: [] }, customParams);
customNotification = notifier.custom(customText, noActionParams);
expect(customNotification).to.have.property('actions');
expect(customNotification.actions).to.have.length(1);
});

it('defaults type and lifetime for "info" config', function () {
expect(customNotification.type).to.be('info');
expect(customNotification.lifetime).to.be(5000);
});

it('dynamic lifetime for "warning" config', function () {
// destroy the default custom notification, avoid duplicate handling
customNotification.clear();

const errorTypeParams = _.defaults({ type: 'warning' }, customParams);
customNotification = notifier.custom(customText, errorTypeParams);
expect(customNotification.type).to.be('warning');
expect(customNotification.lifetime).to.be(10000);
});

it('dynamic type and lifetime for "error" config', function () {
// destroy the default custom notification, avoid duplicate handling
customNotification.clear();

const errorTypeParams = _.defaults({ type: 'error' }, customParams);
customNotification = notifier.custom(customText, errorTypeParams);
expect(customNotification.type).to.be('danger');
expect(customNotification.lifetime).to.be(300000);
});

it('dynamic type and lifetime for "danger" config', function () {
// destroy the default custom notification, avoid duplicate handling
customNotification.clear();

const errorTypeParams = _.defaults({ type: 'danger' }, customParams);
customNotification = notifier.custom(customText, errorTypeParams);
expect(customNotification.type).to.be('danger');
expect(customNotification.lifetime).to.be(300000);
});

it('should wrap the callback functions in a close function', function () {
customNotification.customActions.forEach((action, idx) => {
expect(action.callback).not.to.equal(customParams.actions[idx]);
action.callback();
});
customParams.actions.forEach(action => {
expect(action.callback.called).to.true;
});
});
});

function notify(fnName) {
notifier[fnName](message);
return latestNotification();
Expand Down
81 changes: 75 additions & 6 deletions src/ui/public/notify/notifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ define(function (require) {
return Date.now();
}

function closeNotif(cb, key) {
function closeNotif(notif, cb, key) {
return function () {
// this === notif
let i = notifs.indexOf(this);
let i = notifs.indexOf(notif);
if (i !== -1) notifs.splice(i, 1);
if (this.timerId) this.timerId = clearTO(this.timerId);
if (typeof cb === 'function') cb(key);
Expand All @@ -48,16 +48,24 @@ define(function (require) {
_.set(notif, 'info.version', version);
_.set(notif, 'info.buildNum', buildNum);

if (notif.lifetime !== Infinity) {
if (notif.lifetime !== Infinity && notif.lifetime > 0) {
notif.timerId = setTO(function () {
closeNotif(cb, 'ignore').call(notif);
closeNotif(notif, cb, 'ignore').call(notif);
}, notif.lifetime);
}

notif.clear = closeNotif();
notif.clear = closeNotif(notif);
if (notif.actions) {
notif.actions.forEach(function (action) {
notif[action] = closeNotif(cb, action);
notif[action] = closeNotif(notif, cb, action);
});
} else if (notif.customActions) {
// wrap all of the custom functions in a close
notif.customActions = notif.customActions.map(action => {
return {
key: action.text,
callback: closeNotif(notif, action.callback, action.text)
};
});
}

Expand Down Expand Up @@ -251,6 +259,67 @@ define(function (require) {
}, cb);
};

/**
* Display a custom message
* @param {String} msg - required
* @param {Object} config - required
* @param {Function} cb - optional
*
* config = {
* title: 'Some Title here',
* type: 'info',
* actions: [{
* text: 'next',
* callback: function() { next(); }
* }, {
* text: 'prev',
* callback: function() { prev(); }
* }]
* }
*/
Notifier.prototype.custom = function (msg, config, cb) {
// There is no helper condition that will allow for 2 parameters, as the
// other methods have. So check that config is an object
if (!_.isPlainObject(config)) {
throw new Error('config param is required, and must be an object');
}

// workaround to allow callers to send `config.type` as `error` instead of
// reveal internal implementation that error notifications use a `danger`
// style
if (config.type === 'error') {
config.type = 'danger';
}

const getLifetime = (type) => {
switch (type) {
case 'warning':
return 10000;
case 'danger':
return 300000;
default: // info
return 5000;
}
};

const mergedConfig = _.assign({
type: 'info',
title: 'Notification',
content: formatMsg(msg, this.from),
lifetime: getLifetime(config.type)
}, config);

const hasActions = _.get(mergedConfig, 'actions.length');
if (hasActions) {
mergedConfig.customActions = mergedConfig.actions;
delete mergedConfig.actions;
} else {
mergedConfig.actions = ['accept'];
}

return add(mergedConfig, cb);
};

/**
* Display a debug message
* @param {String} msg [description]
Expand Down
8 changes: 8 additions & 0 deletions src/ui/public/notify/partials/toaster.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
ng-class="'btn-' + notif.type"
ng-click="notif.address()"
>Fix it</button>
<button
type="button"
class="btn"
ng-repeat="action in notif.customActions"
ng-class="'btn-' + notif.type"
ng-click="action.callback()"
ng-bind="action.key"
></button>
</div>
</div>

Expand Down

0 comments on commit e4eb59b

Please sign in to comment.