mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Safely window.confirm when importing saved object
Angular doesn't play nicely with the native thread-blocking functions (e.g. window.confirm) unless the call is wrapped inside a $timeout. A new service exists to allow us to use window.confirm in a safe and consistent way.
This commit is contained in:
parent
f1bea2f6ce
commit
4ac4127b4c
5 changed files with 122 additions and 7 deletions
|
@ -55,6 +55,7 @@ exports.reload = function () {
|
|||
'ui/persisted_log',
|
||||
'ui/private',
|
||||
'ui/promises',
|
||||
'ui/safe_confirm',
|
||||
'ui/state_management/app_state',
|
||||
'ui/state_management/global_state',
|
||||
'ui/storage',
|
||||
|
|
|
@ -4,6 +4,7 @@ define(function (require) {
|
|||
|
||||
require('ui/es');
|
||||
require('ui/promises');
|
||||
require('ui/safe_confirm');
|
||||
require('ui/index_patterns');
|
||||
|
||||
require('ui/modules').get('kibana/courier')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define(function (require) {
|
||||
return function SavedObjectFactory(es, kbnIndex, Promise, Private, Notifier, indexPatterns) {
|
||||
return function SavedObjectFactory(es, kbnIndex, Promise, Private, Notifier, safeConfirm, indexPatterns) {
|
||||
var angular = require('angular');
|
||||
var errors = require('ui/errors');
|
||||
var _ = require('lodash');
|
||||
|
@ -252,12 +252,12 @@ define(function (require) {
|
|||
if (_.get(err, 'origError.status') === 409) {
|
||||
var confirmMessage = 'Are you sure you want to overwrite ' + self.title + '?';
|
||||
|
||||
if (window.confirm(confirmMessage)) { // eslint-disable-line no-alert
|
||||
return docSource.doIndex(source).then(finish);
|
||||
}
|
||||
|
||||
// if the user doesn't overwrite record, just swallow the error
|
||||
return;
|
||||
return safeConfirm(confirmMessage).then(
|
||||
function () {
|
||||
return docSource.doIndex(source).then(finish);
|
||||
},
|
||||
_.noop // if the user doesn't overwrite record, just swallow the error
|
||||
);
|
||||
}
|
||||
return Promise.reject(err);
|
||||
});
|
||||
|
|
82
src/ui/public/safe_confirm/__tests__/safe_confirm.js
Normal file
82
src/ui/public/safe_confirm/__tests__/safe_confirm.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
describe('ui/safe_confirm', function () {
|
||||
var sinon = require('sinon');
|
||||
var expect = require('expect.js');
|
||||
var ngMock = require('ngMock');
|
||||
|
||||
var $rootScope;
|
||||
var $window;
|
||||
var $timeout;
|
||||
var message;
|
||||
var safeConfirm;
|
||||
var promise;
|
||||
|
||||
beforeEach(function () {
|
||||
ngMock.module('kibana', function ($provide) {
|
||||
$provide.value('$window', {
|
||||
confirm: sinon.stub().returns(true)
|
||||
});
|
||||
});
|
||||
|
||||
ngMock.inject(function ($injector) {
|
||||
safeConfirm = $injector.get('safeConfirm');
|
||||
$rootScope = $injector.get('$rootScope');
|
||||
$window = $injector.get('$window');
|
||||
$timeout = $injector.get('$timeout');
|
||||
});
|
||||
|
||||
message = 'woah';
|
||||
|
||||
promise = safeConfirm(message);
|
||||
});
|
||||
|
||||
context('before timeout completes', function () {
|
||||
it('$window.confirm is not invoked', function () {
|
||||
expect($window.confirm.called).to.be(false);
|
||||
});
|
||||
it('returned promise is not resolved', function () {
|
||||
var isResolved = false;
|
||||
function markAsResolved() {
|
||||
isResolved = true;
|
||||
}
|
||||
promise.then(markAsResolved, markAsResolved);
|
||||
$rootScope.$apply(); // attempt to resolve the promise, but this won't flush $timeout promises
|
||||
expect(isResolved).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
context('after timeout completes', function () {
|
||||
it('$window.confirm is invoked with message', function () {
|
||||
$timeout.flush();
|
||||
expect($window.confirm.calledWith(message)).to.be(true);
|
||||
});
|
||||
|
||||
context('when confirmed', function () {
|
||||
it('promise is fulfilled with true', function () {
|
||||
$timeout.flush();
|
||||
|
||||
var value;
|
||||
promise.then(function (v) {
|
||||
value = v;
|
||||
});
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(value).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
context('when canceled', function () {
|
||||
it('promise is rejected with false', function () {
|
||||
$window.confirm.returns(false); // must be set before $timeout.flush()
|
||||
$timeout.flush();
|
||||
|
||||
var value;
|
||||
promise.then(null, function (v) {
|
||||
value = v;
|
||||
});
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(value).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
31
src/ui/public/safe_confirm/safe_confirm.js
Normal file
31
src/ui/public/safe_confirm/safe_confirm.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
define(function (require) {
|
||||
require('ui/modules').get('kibana')
|
||||
|
||||
/*
|
||||
* Angular doesn't play well with thread blocking calls such as
|
||||
* window.confirm() unless those calls are specifically handled inside a call
|
||||
* to $timeout(). Rather than litter the code with that implementation
|
||||
* detail, safeConfirm() can be used.
|
||||
*
|
||||
* WARNING: safeConfirm differs from a native call to window.confirm in that
|
||||
* it only blocks the thread beginning on the next tick. For that reason, a
|
||||
* promise is returned so consumers can handle the control flow.
|
||||
*
|
||||
* Usage:
|
||||
* safeConfirm('This message will be passed to window.confirm()').then(
|
||||
* function () {
|
||||
* // user clicked confirm
|
||||
* },
|
||||
* function () {
|
||||
* // user canceled the confirmation
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
.factory('safeConfirm', function ($window, $timeout, $q) {
|
||||
return function safeConfirm(message) {
|
||||
return $timeout(function () {
|
||||
return $window.confirm(message) || $q.reject(false);
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue