Merge branch 'alert-on-form-leave' into 'master'

Show prompt if user has touched a form and tries to leave page

Closes #377

See merge request !233
This commit is contained in:
Felix Hamann 2019-05-21 22:11:52 +02:00
commit 0cabee0826

View File

@ -247,6 +247,88 @@
setup: interactiveFieldsetUtil,
});
/**
*
* Navigate Away Prompt Utility
* This utility asks the user if (s)he really wants to navigate away
* from a page containing a form if (s)he already touched an input.
* Form-Submits will not trigger the prompt.
* Utility will ignore forms that contain auto submit elements (buttons, inputs).
*
* Attribute: [none]
* (automatically setup on all form tags that dont automatically submit, see AutoSubmitButtonUtil)
*
* Example usage:
* (any page with a form)
*/
var NAVIGATE_AWAY_PROMPT_UTIL_NAME = 'navigateAwayPrompt';
var NAVIGATE_AWAY_PROMPT_UTIL_SELECTOR = 'form';
var NAVIGATE_AWAY_PROMPT_INITIALIZED_CLASS = 'navigate-away-prompt--initialized';
var navigateAwayPromptUtil = function(element) {
var touched = false;
var unloadDueToSubmit = false;
function init() {
if (!element) {
throw new Error('Navigate Away Prompt utility needs to be passed an element!');
}
if (element.classList.contains(NAVIGATE_AWAY_PROMPT_INITIALIZED_CLASS)) {
return false;
}
// ignore forms that get submitted automatically
if (element.querySelector(AUTO_SUBMIT_BUTTON_UTIL_SELECTOR) || element.querySelector(AUTO_SUBMIT_INPUT_UTIL_SELECTOR)) {
return false;
}
window.addEventListener('beforeunload', beforeUnloadHandler);
element.addEventListener('submit', function() {
unloadDueToSubmit = true;
});
element.addEventListener('change', function() {
touched = true;
unloadDueToSubmit = false;
});
// mark initialized
element.classList.add(NAVIGATE_AWAY_PROMPT_INITIALIZED_CLASS);
return {
name: NAVIGATE_AWAY_PROMPT_UTIL_NAME,
element: element,
destroy: function() {
window.removeEventListener('beforeunload', beforeUnloadHandler);
},
};
}
function beforeUnloadHandler(event) {
// allow the event to happen if the form was not touched by the
// user or the unload event was initiated by a form submit
if (!touched || unloadDueToSubmit) {
return false;
}
// cancel the unload event. This is the standard to force the prompt to appear.
event.preventDefault();
// for all non standard compliant browsers we return a truthy value to activate the prompt.
return true;
}
return init();
};
formUtilities.push({
name: NAVIGATE_AWAY_PROMPT_UTIL_NAME,
selector: NAVIGATE_AWAY_PROMPT_UTIL_SELECTOR,
setup: navigateAwayPromptUtil,
});
/**
*
* Auto Submit Button Utility
@ -270,6 +352,10 @@
throw new Error('Auto Submit Button utility needs to be passed an element!');
}
if (element.classList.contains(AUTO_SUBMIT_BUTTON_INITIALIZED_CLASS)) {
return false;
}
// hide and mark initialized
element.classList.add(AUTO_SUBMIT_BUTTON_HIDDEN_CLASS, AUTO_SUBMIT_BUTTON_INITIALIZED_CLASS);
@ -315,6 +401,10 @@
throw new Error('Auto Submit Input utility needs to be passed an element!');
}
if (element.classList.contains(AUTO_SUBMIT_INPUT_INITIALIZED_CLASS)) {
return false;
}
form = element.form;
if (!form) {
throw new Error('Could not determine associated form for auto submit input');
@ -374,6 +464,10 @@
throw new Error('Form Error Remover utility needs to be passed an element!');
}
if (element.classList.contains(FORM_ERROR_REMOVER_INITIALIZED_CLASS)) {
return false;
}
// find form groups
formGroups = Array.from(element.querySelectorAll(FORM_GROUP_SELECTOR));
@ -428,6 +522,8 @@
var DATEPICKER_UTIL_NAME = 'datepicker';
var DATEPICKER_UTIL_SELECTOR = 'input[type="date"], input[type="time"], input[type="datetime-local"]';
var DATEPICKER_INITIALIZED_CLASS = 'datepicker--initialized';
var DATEPICKER_CONFIG = {
"datetime-local": {
enableTime: true,
@ -459,6 +555,10 @@
throw new Error('Datepicker utility needs to be passed an element!');
}
if (element.classList.contains(DATEPICKER_INITIALIZED_CLASS)) {
return false;
}
var flatpickrConfig = DATEPICKER_CONFIG[element.getAttribute("type")];
if (!flatpickrConfig) {
@ -467,6 +567,9 @@
flatpickrInstance = flatpickr(element, flatpickrConfig);
// mark initialized
element.classList.add(DATEPICKER_INITIALIZED_CLASS);
return {
name: DATEPICKER_UTIL_NAME,
element: element,