diff --git a/src/ui/public/autoload/modules.js b/src/ui/public/autoload/modules.js index 888d80a635df3..8413983c1a04a 100644 --- a/src/ui/public/autoload/modules.js +++ b/src/ui/public/autoload/modules.js @@ -20,9 +20,7 @@ import 'ui/parse_query'; import 'ui/persisted_log'; import 'ui/private'; import 'ui/promises'; -import 'ui/dialogues/modal_dialogue'; -import 'ui/dialogues/confirm_dialogue'; -import 'ui/dialogues/safe_confirm'; +import 'ui/dialogues'; import 'ui/state_management/app_state'; import 'ui/state_management/global_state'; import 'ui/storage'; diff --git a/src/ui/public/dialogues/__tests__/safe_confirm.js b/src/ui/public/dialogues/__tests__/safe_confirm.js index 5bb04a98880c3..59524986d5f5c 100644 --- a/src/ui/public/dialogues/__tests__/safe_confirm.js +++ b/src/ui/public/dialogues/__tests__/safe_confirm.js @@ -2,7 +2,7 @@ import angular from 'angular'; import expect from 'expect.js'; import ngMock from 'ng_mock'; -describe.only('ui/dialogues/safe_confirm', function () { +describe('ui/dialogues/safe_confirm', function () { let $rootScope; let $window; @@ -25,10 +25,8 @@ describe.only('ui/dialogues/safe_confirm', function () { }); afterEach(function () { - const confirmationDialogElement = document.querySelector('.confirm-dialogue'); - if (confirmationDialogElement) { - const okayButton = confirmationDialogElement.getElementsByTagName('button')[0]; - + const okayButton = document.body.find('[data-test-subj=confirm-dialog-okay-button]'); + if (okayButton) { $rootScope.$digest(); angular.element(okayButton).click(); } @@ -47,11 +45,11 @@ describe.only('ui/dialogues/safe_confirm', function () { }); context('after timeout completes', function () { - it('confirmation dialog is loaded to dom with message', function () { + it('confirmation dialogue is loaded to dom with message', function () { $timeout.flush(); - const confirmationDialogElement = document.querySelector('.confirm-dialogue'); - expect(!!confirmationDialogElement).to.be(true); - const htmlString = confirmationDialogElement.innerHTML; + const confirmationDialogueElement = document.body.find('[data-test-subj=confirm-dialog]'); + expect(!!confirmationDialogueElement).to.be(true); + const htmlString = confirmationDialogueElement.innerHTML; expect(htmlString.indexOf(message)).to.be.greaterThan(0); }); @@ -64,9 +62,7 @@ describe.only('ui/dialogues/safe_confirm', function () { promise.then((v) => { value = v; }); - - const confirmationDialogElement = document.querySelector('.confirm-dialogue'); - const okayButton = confirmationDialogElement.getElementsByTagName('button')[0]; + const okayButton = document.body.find('[data-test-subj=confirm-dialog-okay-button]'); $rootScope.$digest(); angular.element(okayButton).click(); @@ -81,8 +77,7 @@ describe.only('ui/dialogues/safe_confirm', function () { let value; promise.then(null, (v) => { value = v; }); - const confirmationDialogElement = document.querySelector('.confirm-dialogue'); - const noButton = confirmationDialogElement.getElementsByTagName('button')[1]; + const noButton = document.body.find('[data-test-subj=confirm-dialog-cancel-button]'); $rootScope.$digest(); angular.element(noButton).click(); diff --git a/src/ui/public/dialogues/confirm_dialogue.html b/src/ui/public/dialogues/confirm_dialogue.html deleted file mode 100644 index 50c14a6eda2a4..0000000000000 --- a/src/ui/public/dialogues/confirm_dialogue.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
-
- {{message}} -
-
- - -
-
diff --git a/src/ui/public/dialogues/confirm_dialogue.js b/src/ui/public/dialogues/confirm_dialogue.js deleted file mode 100644 index e2cb00a78a92c..0000000000000 --- a/src/ui/public/dialogues/confirm_dialogue.js +++ /dev/null @@ -1,63 +0,0 @@ -import uiModules from 'ui/modules'; -import confirmDialogueTemplate from './confirm_dialogue.html'; - -const module = uiModules.get('kibana'); - -module.factory('showConfirmDialogue', function (ModalDialogue, $q, $compile, $rootScope) { - /** - * Shows a modal confirmation dialog with the given message. - * - * @param {String} message - * @param {String} yesButtonText - Defaults to Okay, supply a custom string to override. - * @param {String} noButtonText - Defaults to Cancel, supply a custom string to override. - * @return {Promise} Returns a promise that will be resolved to true if the user - * clicks the yes/okay button and rejected if the user clicks the no/cancel button. - */ - return function showConfirmDialogue(message, yesButtonText = 'Okay', noButtonText = 'Cancel') { - const deferred = $q.defer(); - - const dialogueScope = $rootScope.$new(); - - let modalDialogue = undefined; - - dialogueScope.onYes = () => { - dialogueScope.$destroy(); - modalDialogue.destroy(); - deferred.resolve(true); - }; - - dialogueScope.onNo = () => { - dialogueScope.$destroy(); - modalDialogue.destroy(); - deferred.reject(false); - }; - - dialogueScope.message = message; - - const confirmHtml = - ` - `; - const element = $compile(confirmHtml)(dialogueScope); - modalDialogue = new ModalDialogue(element); - - return deferred.promise; - }; -}); - -module.directive('confirmDialogue', function () { - return { - restrict: 'E', - template: confirmDialogueTemplate, - scope: { - message: '@', - yesButtonText: '@', - noButtonText: '@', - onYes: '&', - onNo: '&' - } - }; -}); diff --git a/src/ui/public/dialogues/confirm_dialogue_container.html b/src/ui/public/dialogues/confirm_dialogue_container.html new file mode 100644 index 0000000000000..c209b5bd5b987 --- /dev/null +++ b/src/ui/public/dialogues/confirm_dialogue_container.html @@ -0,0 +1,6 @@ + + diff --git a/src/ui/public/dialogues/directives/confirm_dialogue.html b/src/ui/public/dialogues/directives/confirm_dialogue.html new file mode 100644 index 0000000000000..95e7469215934 --- /dev/null +++ b/src/ui/public/dialogues/directives/confirm_dialogue.html @@ -0,0 +1,15 @@ +
+
+
+ {{message}} +
+
+ + +
+
+
diff --git a/src/ui/public/dialogues/directives/confirm_dialogue.js b/src/ui/public/dialogues/directives/confirm_dialogue.js new file mode 100644 index 0000000000000..56da72981bd60 --- /dev/null +++ b/src/ui/public/dialogues/directives/confirm_dialogue.js @@ -0,0 +1,18 @@ +import uiModules from 'ui/modules'; +import confirmDialogueTemplate from './confirm_dialogue.html'; + +const module = uiModules.get('kibana'); + +module.directive('confirmDialogue', function () { + return { + restrict: 'E', + template: confirmDialogueTemplate, + scope: { + message: '@', + confirmButtonText: '@', + cancelButtonText: '@', + onConfirm: '&', + onCancel: '&' + } + }; +}); diff --git a/src/ui/public/dialogues/index.js b/src/ui/public/dialogues/index.js new file mode 100644 index 0000000000000..8423442f6c769 --- /dev/null +++ b/src/ui/public/dialogues/index.js @@ -0,0 +1 @@ +import './safe_confirm.factory'; diff --git a/src/ui/public/dialogues/modal_dialogue.factory.js b/src/ui/public/dialogues/modal_dialogue.factory.js new file mode 100644 index 0000000000000..f9e146789017a --- /dev/null +++ b/src/ui/public/dialogues/modal_dialogue.factory.js @@ -0,0 +1,12 @@ +import uiModules from 'ui/modules'; +import { ModalDialogue } from './modal_dialogue'; + +const module = uiModules.get('kibana'); + +module.factory('ModalDialogue', function ($rootScope, $compile) { + return class AngularModalDialogue extends ModalDialogue { + constructor(html, scopeObject) { + super(html, scopeObject, $rootScope, $compile); + } + }; +}); diff --git a/src/ui/public/dialogues/modal_dialogue.js b/src/ui/public/dialogues/modal_dialogue.js index 30f34f85f08e2..40a29dc6caae3 100644 --- a/src/ui/public/dialogues/modal_dialogue.js +++ b/src/ui/public/dialogues/modal_dialogue.js @@ -1,29 +1,27 @@ -import uiModules from 'ui/modules'; import angular from 'angular'; -const module = uiModules.get('kibana'); - -module.factory('ModalDialogue', function () { +export class ModalDialogue { /** - * The only thing this class does is load an element onto the dom when instantiated, - * and removes it when destroy is called. Useful for the modal confirmation dialog. - * Long term, some of the shared modal complexity could be moved in here, hence the name. + * + * @param html + * @param scopeObject + * @param $rootScope + * @param $compile {function} */ - return class ModalDialogue { - /** - * - * @param innerElement {HTMLElement} - an element that will be appended to the dom. - */ - constructor(innerElement) { - this.innerElement = innerElement; - angular.element(document.body).append(this.innerElement); - } + constructor(html, scopeObject, $rootScope, $compile) { + this.modalScope = $rootScope.$new(); + + Object.keys(scopeObject).forEach((key) => { this.modalScope[key] = scopeObject[key]; }); + + this.innerElement = $compile(html)(this.modalScope); + angular.element(document.body).append(this.innerElement); + } - /** - * Removes the element from the dom. - */ - destroy() { - this.innerElement.remove(); - } - }; -}); + /** + * Removes the element from the dom. + */ + destroy() { + this.innerElement.remove(); + this.modalScope.$destroy(); + } +} diff --git a/src/ui/public/dialogues/safe_confirm.factory.js b/src/ui/public/dialogues/safe_confirm.factory.js new file mode 100644 index 0000000000000..fbdd9144a79b3 --- /dev/null +++ b/src/ui/public/dialogues/safe_confirm.factory.js @@ -0,0 +1,11 @@ +import uiModules from 'ui/modules'; +import { safeConfirm } from './safe_confirm'; +import './directives/confirm_dialogue'; +import './modal_dialogue.factory'; + +const module = uiModules.get('kibana'); + +module.factory('safeConfirm', function ($timeout, ModalDialogue, Promise) { + return (message, confirmButtonText = 'Okay', cancelButtonText = 'Cancel') => + $timeout(() => safeConfirm(message, confirmButtonText, cancelButtonText, Promise, ModalDialogue)); +}); diff --git a/src/ui/public/dialogues/safe_confirm.js b/src/ui/public/dialogues/safe_confirm.js index 581882af9563b..2409d49fc9c66 100644 --- a/src/ui/public/dialogues/safe_confirm.js +++ b/src/ui/public/dialogues/safe_confirm.js @@ -1,29 +1,34 @@ -import uiModules from 'ui/modules'; -uiModules.get('kibana') +import confirmDialogueContainerTemplate from './confirm_dialogue_container.html'; -/* - * Angular doesn't play well with thread blocking calls such as - * unless those calls are specifically handled inside a call - * to $timeout(). Rather than litter the code with that implementation - * detail, safeConfirm() can be used. +/** + * Shows a modal confirmation dialogue with the given message. * - * WARNING: safeConfirm 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 shown in a modal dialog').then( - * function () { - * // user clicked the okay button - * }, - * function () { - * // user canceled the confirmation - * } - * ); + * @param {String} message + * @param {String} confirmButtonText - Text to show for the button that will resolve the returned promise + * @param {String} cancelButtonText - Text to show for the button that will reject the returned promise + * @param {Promise} Promise - a promise class. + * @param {ModalDialogue} ModalDialogue service + * @return {Promise} Returns an angular promise that will be resolved to true if the user + * clicks the yes/okay button and rejected if the user clicks the no/cancel button. */ -.factory('safeConfirm', function ($window, $timeout, $q, showConfirmDialogue) { - return function safeConfirm(message) { - return $timeout(function () { - return showConfirmDialogue(message) || $q.reject(false); - }); - }; -}); +export function safeConfirm(message, confirmButtonText, cancelButtonText, Promise, ModalDialogue) { + return new Promise((resolve, reject) => { + let modalDialogue = undefined; + + const dialogueScope = { + onConfirm: () => { + modalDialogue.destroy(); + resolve(true); + }, + onCancel: () => { + modalDialogue.destroy(); + reject(false); + }, + message, + confirmButtonText, + cancelButtonText + }; + + modalDialogue = new ModalDialogue(confirmDialogueContainerTemplate, dialogueScope); + }); +} diff --git a/src/ui/public/styles/confirm-dialog.less b/src/ui/public/styles/confirm-dialog.less index 875116bdfc5ca..6feb8161cb790 100644 --- a/src/ui/public/styles/confirm-dialog.less +++ b/src/ui/public/styles/confirm-dialog.less @@ -1,30 +1,34 @@ @import "~ui/styles/variables"; .confirm-dialogue { - z-index: 10; - position: absolute; background-color: white; - left: 40%; - top: 30%; width: 400px; - padding: 15px 15px 0px 15px; + padding: 15px; +} - .message { - padding: 0px 0px 5px 0px; - } +.confirm-dialogue__message { + padding: 0px 0px 5px 0px; +} + +.confirm-dialogue__actions { + display: flex; + justify-content: flex-end; - .form-group { - float: right; + > * + * { + margin-left: 5px; } } .disable-app-layer { - z-index: 9; - position: absolute; - opacity: .5; - background-color: @kibanaGray1; - height: 100%; - width: 100%; + display: flex; + align-items: center; + justify-content: center; + position: fixed; + z-index: 10; + background-color: rgba(0, 0, 0, 0.3); top: 0; left: 0; + bottom: 0; + right: 0; + padding-bottom: 10vh; }