Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce custom modal confirmation dialogs #9859

Merged
merged 10 commits into from
Jan 19, 2017

Conversation

stacey-gammon
Copy link
Contributor

Stop using native confirm dialogs and create a custom one. @cjcenizal is planning on further improving the UI of the dialog but here it is for now:

screen shot 2017-01-12 at 4 52 30 pm

You can test it out in timelion by trying to delete a saved sheet. All usages of safeConfirm will now use this custom modal dialog.

Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweeeet! I had a lot of really small suggestions, and a couple larger ones that were centered on the UI.

My suggestions about changing the modal markup and styles will result in the modal looking like this:

image

width: 100%;
top: 0;
left: 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change these styles to:

.confirm-dialogue {
  background-color: white;
  width: 400px;
  padding: 15px;
}

  .confirm-dialogue__message {
    padding: 0px 0px 5px 0px;
  }

  .confirm-dialogue__actions {
    display: flex;
    justify-content: flex-end;

    > * + * {
      margin-left: 5px;
    }
  }

.disable-app-layer {
  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;
}

<button class="btn btn-primary" ng-click="onYes()">{{yesButtonText}}</button>
<button class="btn" ng-click="onNo()">{{noButtonText}}</button>
</div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change this markup to:

<div class="disable-app-layer">
  <div class="confirm-dialogue">
    <div class="confirm-dialogue__message">
      {{message}}
    </div>
    <div class="confirm-dialogue__actions">
      <button class="kuiButton kuiButton--primary" ng-click="onYes()">{{yesButtonText}}</button>
      <button class="kuiButton kuiButton--basic" ng-click="onNo()">{{noButtonText}}</button>
    </div>
  </div>
</div>

* @return {Promise<boolean>} 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') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the default for the yesButtonText. Confirmation actions should always explicitly state what will happen to help clarify the consequences for the user.

It shouldn't be hard to update the rest of Kibana to reflect this change since it looks like it's only used in a couple other places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree on this one. :) I don't want to require callers to pass in text manually on every call site. If we ever wanted to changed that default text, we'd have to do so everywhere.

$timeout.flush();
expect($window.confirm.calledWith(message)).to.be(true);
const confirmationDialogElement = document.querySelector('.confirm-dialogue');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using classes we should use a data-test-subj="confirmDialogue" attribute selector. This uncouples our tests from our CSS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, should be confirmationDialogueElement (sp).

$rootScope.$apply();

const confirmationDialogElement = document.querySelector('.confirm-dialogue');
const okayButton = confirmationDialogElement.getElementsByTagName('button')[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also use data-test-subj selectors to find these buttons. This will uncouple our tests from our elements and from the order in which they appear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

@@ -29,10 +24,17 @@ describe('ui/safe_confirm', function () {
promise = safeConfirm(message);
});

afterEach(function () {
const confirmationDialogElement = document.querySelector('.confirm-dialogue');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be confirmationDialogueElement (sp)?

@@ -45,35 +47,45 @@ describe('ui/safe_confirm', function () {
});

context('after timeout completes', function () {
it('$window.confirm is invoked with message', function () {
it('confirmation dialog is loaded to dom with message', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"dialogue"?

dialogueScope.onYes = () => {
dialogueScope.$destroy();
modalDialogue.destroy();
deferred.resolve(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need to return true and false when we resolve and reject? I think we can remove them since they don't seem to serve a purpose. This will allow us to simplify the tests a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that to match the prior functionality since it was tested, and I wanted to keep it how it was before. Otherwise I have to check all the call sites and make sure all usages of safeConfirm aren't relying on true/false. I thought it was silly to, to pass this, just worried about breaking something. If you feel strongly about it though, I can do it.

* function () {
* // user clicked confirm
* // user clicked the okay button
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original comment was better here, because "Okay" is too coupled to the UI, but "confirm" is descriptive of the concept.

* promise is returned so consumers can handle the control flow.
*
* Usage:
* safeConfirm('This message will be passed to window.confirm()').then(
* safeConfirm('This message will be shown in a modal dialog').then(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sp: "dialogue"

@stacey-gammon
Copy link
Contributor Author

@cjcenizal Addressed most of your requested changes, and also tried to implement the new style guide after discussing some stuff with @w33ble and he had some good other suggestions. Please take another look and let me know what you think!

Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Joe and I talked about the most recent changes to the PR and we came to a couple conclusions:

  1. Logic is best extracted out of an Angular service into a vanilla JS service when the resulting JS service has no Angular dependencies. This makes the service more reusable, testable, and easier to migrate into a non-Angular codebase.
  2. Extracting logic into a vanilla JS service creates indirection and adds complexity, which is only a good tradeoff if the above criteria can be met.
  3. The file modal_dialogue.js is more of a "teleport" for adding and removing an element to the body, and its knowledge of scope dilutes this single responsibility.

So, I think we should revert to the most recent commit, but preserve the changes made in response to my comments, as well as preserve the single point of entry in modules.js.

Let's Zoom about this some more to make sure this makes sense. We had a long discussion and I may not be completely representing it here.

Joe's working on a POC to explore some other ideas he had as we were talking, partially inspired by the Modal demo I showed you (http://smaato.github.io/ui-framework/#/modal).

@@ -29,10 +24,15 @@ describe('ui/safe_confirm', function () {
promise = safeConfirm(message);
});

afterEach(function () {
const okayButton = document.body.find('[data-test-subj=confirm-dialog-okay-button]');
Copy link
Contributor

@cjcenizal cjcenizal Jan 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's a bit of a mixed-bag right now, but most of the data-test-subj selectors are camel case and I think we should update the few ones which are snake case to be camel case at some point. So let's make these new ones camel case.

message="{{message}}"
confirm-button-text="{{confirmButtonText}}"
cancel-button-text="{{cancelButtonText}}">
</confirm-dialogue>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we format this per the HTML style guide:

<confirm-dialogue
  on-confirm="onConfirm()"
  on-cancel="onCancel()"
  message="{{message}}"
  confirm-button-text="{{confirmButtonText}}"
  cancel-button-text="{{cancelButtonText}}"
></confirm-dialogue>

ng-click="onConfirm()">{{confirmButtonText}}</button>
<button class="kuiButton kuiButton--basic"
data-test-subj="confirm-dialog-cancel-button"
ng-click="onCancel()">{{cancelButtonText}}</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here:

      <button
        class="kuiButton kuiButton--primary"
        data-test-subj="confirm-dialog-okay-button"
        ng-click="onConfirm()"
      >
        {{confirmButtonText}}
      </button>
      <button
        class="kuiButton kuiButton--basic"
        data-test-subj="confirm-dialog-cancel-button"
        ng-click="onCancel()"
      >
        {{cancelButtonText}}
      </button>


module.factory('safeConfirm', function ($timeout, ModalDialogue, Promise) {
return (message, confirmButtonText = 'Okay', cancelButtonText = 'Cancel') =>
$timeout(() => safeConfirm(message, confirmButtonText, cancelButtonText, Promise, ModalDialogue));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I need some education here... why are we wrapping it in a $timeout? Is it just to trigger a digest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have kept the original comment. I'm not 100% sure myself, but sounded necessary. Here is the original comment:

/*
 * 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.
 *
 * 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
 *    }
 *  );
 */

It's possible it's not even necessary since we are using promises and not a thread blocking call.

const module = uiModules.get('kibana');

module.factory('safeConfirm', function ($timeout, ModalDialogue, Promise) {
return (message, confirmButtonText = 'Okay', cancelButtonText = 'Cancel') =>
Copy link
Contributor

@cjcenizal cjcenizal Jan 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hear you... but my point is that "Okay" is generally not an acceptable label for this button, from a UX standpoint. The text should always explicitly state the action that's going to be taken, so the user only needs to read the button to know what clicking it does. So by supporting a default, we're encouraging the opposite of what we want from a UX standpoint.

I looked for the cases where safeConfirm is used to see where we'd need to make changes. Here's what I found:

  • In the case of Timelion, this should be "Delete the sheet ${sheetTitle}"
  • For overwriting Saved Objects, this should be "Overwrite ${objectTitle}"
  • For index patterns, it should be "Overwrite"
  • For Graph when clearing a workspace, it should be "Clear workspace". When removing a drill-down, it should be "Remove drilldown". When deleting a workspace, it should be "Delete workspace".

I can understand wanting to avoid bloating this PR, so we can do this in a separate step, but I think it's a really important change to make.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I understand your point better now and I'm sorry for reading your original comment too quickly - after re-reading it, you made yourself clear, so that was my bad. I thought you wanted me to propagate 'Okay' and 'Cancel' everywhere.

I'm all for it, but since this PR does match prior functionality, I'd prefer to make the change in a separate PR.

@tbragin tbragin added the Team:Platform-Design Team Label for Kibana Design Team. Support the Analyze group of plugins. label Jan 14, 2017
@stacey-gammon
Copy link
Contributor Author

Are the new rules/suggestions using this .factory style/wrapper written down anywhere? If not I think we should keep track of the questions that arise while we experiment with it, and keep track of the answers decided on. For instance don't extricate vanilla js if it's tightly bound to angular.

After the evolving codebase email that went around at the end of last week, I think the sooner we can embrace this team wide, the better. I was thinking about that part of the article @Bargs shared If you don't know where you're going, you're lost and incrementalism is just a delusion to trick you into thinking you're making meaningful progress.. If we don't have team wide adoption of this style then I'm just introducing further disparities into the code base - directly going against what @Bargs said in that email thread "for existing plugins/apps consistency really does trump everything else".

Also @cjcenizal you had said in that email a new pattern's success depends on two criteria: a) understanding and embrace by the team, and b) consistent application by the team throughout the codebase.

I don't think we've hit a) yet with this new pattern. I like it a lot, but after thinking about that email, I'm not sure I should be adopting it at this point. I guess I don't see the difference between me introducing this new pattern/style and what @weltenwort was trying to do. Both are attempts at creating new patterns in the code base but it looks like @weltenwort was held to the consistency/team adoption standard, while this pattern is being allowed to slip by. Perhaps this is my fault since I am the first person to introduce it to the existing code base, I'm just having second thoughts after that email and wondering if adopting the .factory.js files at this point is the right move.

@stacey-gammon
Copy link
Contributor Author

I merged @w33ble's PR and made a few small adjustments like moving safe_confirm into the modals folder.

I have a question though - why the change to pass onConfirm and onCancel instead of returning a promise? Also making them default to noop feels a bit weird to me. If the default is to do nothing, there really shouldn't be two buttons. I could see making one optional, but not both. But then if you make one optional, which one?

stacey-gammon and others added 5 commits January 17, 2017 09:30
and implement new angular style suggestions from Joe
no point making vanilla code that's really only going to work with angular i guess...

also broke apart the confirm modal from the injector, and made safeConfirm just use the confirm modal
 - Move safe_confirm into modals folder
 - make data-test-subj camelCase.
 - get rid of $timeout call, doesn’t seem necessary anymore, now that
window.confirm is not being used.
@w33ble
Copy link
Contributor

w33ble commented Jan 17, 2017

Are the new rules/suggestions using this .factory style/wrapper written down anywhere? If not I think we should keep track of the questions that arise while we experiment with it, and keep track of the answers decided on. For instance don't extricate vanilla js if it's tightly bound to angular.

This is all still very much in flux, it's just a pattern we've been happy with elsewhere. I still would have liked to break out the vanilla code, even just to make testing easier, but I see CJ's point in that the code is useless without Angular. But mocking the Angular stuff isn't that hard either, so I'm a little torn. What do you think?

If we don't have team wide adoption of this style then I'm just introducing further disparities into the code base - directly going against what @Bargs said in that email thread "for existing plugins/apps consistency really does trump everything else".

I agree with what @Bargs has said. I didn't know of anyone else proposing other patterns at the moment though, and still need to dive in to what @weltenwort has been doing. Planning to sync with him this week. Like I said, the management team has been pretty happy with these patterns, but we do need to get larger buy-in from the team, and I think it's important to get feedback from more than just the three of us; that's a big part of why I brought this up with you originally. We're still very much open to other ideas.

If you decide not to follow our patterns here, it's really up to you. This is your PR after all.

I have a question though - why the change to pass onConfirm and onCancel instead of returning a promise?

I though it best to let the click handlers decide what to do when the user clicks the button. This way, the modal is only concerned with executing some handler and closing itself. We may not always want to deal with a promise, and it has the added bonus of one less dependency within confirm_modal.

If you disagree, feel free to change it up.

Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had some thoughts.

}
promise.then(markAsResolved, markAsResolved);
$rootScope.$apply();
expect(isResolved).to.be(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use spies here:

import sinon from 'sinon';

const callback = sinon.spy();
promise.then(callback, callback);
$rootScope.$apply();
expect(callback.called).to.be(false);

it('confirmation dialogue is loaded to dom with message', function () {
$rootScope.$digest();
const confirmationDialogueElement = angular.element(document.body).find('[data-test-subj=confirmModal]');
expect(!!confirmationDialogueElement).to.be(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not expect(confirmationDialogueElement).to.not.be(undefined);? It seems clearer to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, much better.

$rootScope.$digest();
confirmButton.click();

expect(confirmed).to.be(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use a spy here too.

$rootScope.$digest();
noButton.click();

expect(rejected).to.be(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be done with a spy.


return function confirmModal(message, onConfirm = noop, onCancel = noop, confirmButtonText = 'Confirm', cancelButtonText = 'Cancel') {

if (modalPopover) throw new Error('Ah ah ah, only one modal, buddy!');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this error message clearer? E.g. "You've called confirmModal but there's already a modal open. You can only have one modal open at a time."

export class Popover {
constructor(element) {
this.element = element;
angular.element(document.body).append(this.element);
Copy link
Contributor

@cjcenizal cjcenizal Jan 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One killer improvement would be to rename this file modal_overlay.js and rename the class ModalOverlay. Then we could make this logic responsible for creating the modal overlay wrapper element:

this.element = /* don't remember how to create an element, but that's what we should do here, with <div class="modalOverlay"></div> */;
this.element.append(element);

@stacey-gammon
Copy link
Contributor Author

@cjcenizal @w33ble thanks for the chat today, helped a lot. Code should be updated as discussed plus most recent comments.

@@ -0,0 +1,3 @@
import './confirm_modal';
import './safe_confirm';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having safe_confirm in modals seems weird. The safe_confirm is still its own service, the fact that is uses a modal now is an implementation detail...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was always a modal, it's just that now it uses our custom modal. Even if we left it using the native modal I think it could still go in a modals folder.

There is nothing else that went in the previous safe_confirm folder (except tests) so it doesn't deserve a dedicated extra folder IMO. Is there another location you think it could go that isn't in it's own dedicated folder?

@w33ble
Copy link
Contributor

w33ble commented Jan 17, 2017

Overall, this looks great!

One UX detail - it would be great if safeConfirm/confirmModal would focus the Okay/Confirm button by default, so users could just hit enter and confirm the dialog. This is how the vanilla window.confirm dialog works. Breaking that and requiring a click would be super annoying to anyone used to the default confirm dialog.

@stacey-gammon
Copy link
Contributor Author

@w33ble good point about default focus, fixed!

@w33ble
Copy link
Contributor

w33ble commented Jan 18, 2017

Love this change. LGTM!

Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have one stupid suggestion; then merge away!

<div class="kuiModal" style="width: 450px" data-test-subj="confirmModal">
<div class="kuiModalBody">
<div class="kuiModalBodyText">
{{message}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be indented once.

@stacey-gammon stacey-gammon merged commit 6b009a1 into elastic:master Jan 19, 2017
elastic-jasper added a commit that referenced this pull request Jan 19, 2017
Backports PR #9859

**Commit 1:**
Introduce custom modal confirmation dialogs

* Original sha: 6b727df
* Authored by Stacey Gammon <[email protected]> on 2017-01-13T15:56:44Z

**Commit 2:**
address code review comments

and implement new angular style suggestions from Joe

* Original sha: 5ca36df
* Authored by Stacey Gammon <[email protected]> on 2017-01-13T22:12:44Z

**Commit 3:**
refactor

no point making vanilla code that's really only going to work with angular i guess...

also broke apart the confirm modal from the injector, and made safeConfirm just use the confirm modal

* Original sha: ce783b2
* Authored by Joe Fleming <[email protected]> on 2017-01-14T01:09:52Z
* Committed by Stacey Gammon <[email protected]> on 2017-01-17T14:30:21Z

**Commit 4:**
Clean up from merge

 - Move safe_confirm into modals folder
 - make data-test-subj camelCase.
 - get rid of $timeout call, doesn’t seem necessary anymore, now that
window.confirm is not being used.

* Original sha: c91fbc6
* Authored by Stacey Gammon <[email protected]> on 2017-01-16T15:00:14Z

**Commit 5:**
Fix tests

* Original sha: e581374
* Authored by Stacey Gammon <[email protected]> on 2017-01-16T15:42:41Z

**Commit 6:**
Use ui-framework styles

* Original sha: d1e2b12
* Authored by Stacey Gammon <[email protected]> on 2017-01-17T15:22:52Z

**Commit 7:**
Address latest round of comments and remove adoption of new patterns.

* Original sha: 0b690f1
* Authored by Stacey Gammon <[email protected]> on 2017-01-17T20:00:52Z

**Commit 8:**
Improve error message. Use sinon in tests.

* Original sha: 92f815d
* Authored by Stacey Gammon <[email protected]> on 2017-01-17T20:07:22Z

**Commit 9:**
Focus on the confirm button by default

* Original sha: 27be4df
* Authored by Stacey Gammon <[email protected]> on 2017-01-18T14:17:44Z

**Commit 10:**
Indent message

* Original sha: 3dc6c93
* Authored by Stacey Gammon <[email protected]> on 2017-01-18T22:04:49Z
stacey-gammon pushed a commit that referenced this pull request Jan 19, 2017
Backports PR #9859

**Commit 1:**
Introduce custom modal confirmation dialogs

* Original sha: 6b727df
* Authored by Stacey Gammon <[email protected]> on 2017-01-13T15:56:44Z

**Commit 2:**
address code review comments

and implement new angular style suggestions from Joe

* Original sha: 5ca36df
* Authored by Stacey Gammon <[email protected]> on 2017-01-13T22:12:44Z

**Commit 3:**
refactor

no point making vanilla code that's really only going to work with angular i guess...

also broke apart the confirm modal from the injector, and made safeConfirm just use the confirm modal

* Original sha: ce783b2
* Authored by Joe Fleming <[email protected]> on 2017-01-14T01:09:52Z
* Committed by Stacey Gammon <[email protected]> on 2017-01-17T14:30:21Z

**Commit 4:**
Clean up from merge

 - Move safe_confirm into modals folder
 - make data-test-subj camelCase.
 - get rid of $timeout call, doesn’t seem necessary anymore, now that
window.confirm is not being used.

* Original sha: c91fbc6
* Authored by Stacey Gammon <[email protected]> on 2017-01-16T15:00:14Z

**Commit 5:**
Fix tests

* Original sha: e581374
* Authored by Stacey Gammon <[email protected]> on 2017-01-16T15:42:41Z

**Commit 6:**
Use ui-framework styles

* Original sha: d1e2b12
* Authored by Stacey Gammon <[email protected]> on 2017-01-17T15:22:52Z

**Commit 7:**
Address latest round of comments and remove adoption of new patterns.

* Original sha: 0b690f1
* Authored by Stacey Gammon <[email protected]> on 2017-01-17T20:00:52Z

**Commit 8:**
Improve error message. Use sinon in tests.

* Original sha: 92f815d
* Authored by Stacey Gammon <[email protected]> on 2017-01-17T20:07:22Z

**Commit 9:**
Focus on the confirm button by default

* Original sha: 27be4df
* Authored by Stacey Gammon <[email protected]> on 2017-01-18T14:17:44Z

**Commit 10:**
Indent message

* Original sha: 3dc6c93
* Authored by Stacey Gammon <[email protected]> on 2017-01-18T22:04:49Z
@stacey-gammon stacey-gammon deleted the modal-dialogs branch April 6, 2017 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review Team:Platform-Design Team Label for Kibana Design Team. Support the Analyze group of plugins. v5.3.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants