Service to show confirmation prompt when leaving a dirty form (#16688)

* 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
This commit is contained in:
Shaunak Kashyap 2018-02-13 07:21:20 -08:00 committed by GitHub
parent 0ca6b5521d
commit d250e661f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 0 deletions

View file

@ -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);
});

View file

@ -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);
}
}

View file

@ -0,0 +1 @@
import './dirty_prompt.factory';

View file

@ -12,6 +12,7 @@
<a
class="kuiLocalBreadcrumb__link"
href="{{ breadcrumb.href }}"
data-test-subj="lnkBreadcrumb{{$index}}"
>
{{ breadcrumb.display }}
</a>