Skip to content

Commit

Permalink
Service to show confirmation prompt when leaving a dirty form (#16688)
Browse files Browse the repository at this point in the history
* Service to show confirmation prompt when leaving a dirty form

* Add data test subject for breadcrumbs

This will enable testing the dirty prompt service via functional tests by providing a means for the user to navigate away via the breadcrumbs
  • Loading branch information
ycombinator authored Feb 13, 2018
1 parent 0ca6b55 commit d250e66
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/ui/public/dirty_prompt/dirty_prompt.factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { uiModules } from 'ui/modules';
import { DirtyPrompt } from './dirty_prompt';

uiModules.get('kibana')
.factory('dirtyPrompt', ($injector) => {
const $window = $injector.get('$window');
const confirmModal = $injector.get('confirmModal');
const $rootScope = $injector.get('$rootScope');

return new DirtyPrompt($window, $rootScope, confirmModal);
});
80 changes: 80 additions & 0 deletions src/ui/public/dirty_prompt/dirty_prompt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { noop } from 'lodash';

const confirmMessage = `You have unsaved changes. Proceed and discard changes?`;

function registerUrlChangeHandler(checkDirty) {
this.beforeUnloadHandler = (event) => {
if (checkDirty()) {
// Browsers do not honor the message you set here. The only requirement
// is that is is not an empty string. I am just using the confirmMessage
// here for consistency
event.returnValue = confirmMessage;
}
};

// When the user navigates to an external url or another app, we must
// rely on the build-in beforeunload confirmation dialog. We do not have
// the ability to change the text or appearance of this dialog.
this.$window.addEventListener('beforeunload', this.beforeUnloadHandler);
}

function deregisterUrlChangeHandler() {
this.$window.removeEventListener('beforeunload', this.beforeUnloadHandler);
}

function registerRouteChangeHandler(checkDirty) {
// When the user navigates within the same app, we can present them with
// a friendly confirmation dialog box
const deregister = this.$rootScope.$on('$locationChangeStart', (event, newUrl) => {
if (!checkDirty()) {
return;
}

// At this point, we know the dirty prompt should be shown, so
// cancel the location change event, and keep the user at
// their current location
event.preventDefault();

// Notify user about unsaved changes and ask the user for confirmation
// about navigating away (changing their location) anyway
const confirmModalOptions = {
onConfirm: () => {
this.deregister();
this.$window.location.href = newUrl;
},
confirmButtonText: 'Discard Changes'
};

return this.confirmModal(confirmMessage, confirmModalOptions);
});

this.deregisterListener = deregister;
}

function deregisterRouteChangeHandler() {
this.deregisterListener();
}

export class DirtyPrompt {
constructor($window, $rootScope, confirmModal) {
this.$window = $window;
this.$rootScope = $rootScope;
this.confirmModal = confirmModal;
this.deregisterListener = noop;
this.beforeUnloadHandler = noop;
}

/**
* @param checkDirty function which returns a bool to call to
* determine dirty state
*/
register = (checkDirty) => {
registerUrlChangeHandler.call(this, checkDirty);
registerRouteChangeHandler.call(this, checkDirty);
}

deregister = () => {
deregisterUrlChangeHandler.call(this);
deregisterRouteChangeHandler.call(this);
}
}
1 change: 1 addition & 0 deletions src/ui/public/dirty_prompt/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './dirty_prompt.factory';
1 change: 1 addition & 0 deletions src/ui/public/kbn_top_nav/bread_crumbs/bread_crumbs.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<a
class="kuiLocalBreadcrumb__link"
href="{{ breadcrumb.href }}"
data-test-subj="lnkBreadcrumb{{$index}}"
>
{{ breadcrumb.display }}
</a>
Expand Down

0 comments on commit d250e66

Please sign in to comment.