From 559fb3fb9d91072ebbb095ad2a16f103ccb9b824 Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Tue, 9 Apr 2019 21:55:58 +0200 Subject: [PATCH] refactor async form js util --- src/Foundation.hs | 1 + src/Handler/Admin.hs | 2 +- src/Handler/Home.hs | 2 +- static/css/utils/asyncForm.scss | 22 +++++----- static/js/utils/asyncForm.js | 78 +++++++++++++++++++++++---------- 5 files changed, 69 insertions(+), 36 deletions(-) diff --git a/src/Foundation.hs b/src/Foundation.hs index 22af31b8f..70155f9a4 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -1071,6 +1071,7 @@ siteLayout' headingOverride widget = do -- addScript $ StaticR js_utils_alerts_js -- addScript $ StaticR js_utils_asidenav_js -- JavaScript utils + addScript $ StaticR js_utils_asyncForm_js addScript $ StaticR js_utils_asyncTable_js addScript $ StaticR js_utils_checkAll_js addScript $ StaticR js_utils_form_js diff --git a/src/Handler/Admin.hs b/src/Handler/Admin.hs index 19b84adf3..653f91979 100644 --- a/src/Handler/Admin.hs +++ b/src/Handler/Admin.hs @@ -121,7 +121,7 @@ postAdminTestR = do let emailWidget' = wrapForm emailWidget def { formAction = Just . SomeRoute $ AdminTestR , formEncoding = emailEnctype - , formAttrs = [("data-ajax-submit", "")] + , formAttrs = [("uw-async-form", "")] } diff --git a/src/Handler/Home.hs b/src/Handler/Home.hs index 7b60a6da3..81dc1ac66 100644 --- a/src/Handler/Home.hs +++ b/src/Handler/Home.hs @@ -261,7 +261,7 @@ postHelpR = do let form = wrapForm formWidget def { formAction = Just $ SomeRoute HelpR , formEncoding = formEnctype - , formAttrs = [ ("data-ajax-submit", "") | isModal ] + , formAttrs = [ ("uw-async-form", "") | isModal ] } formResultModal res HelpR $ \HelpForm{..} -> do diff --git a/static/css/utils/asyncForm.scss b/static/css/utils/asyncForm.scss index a0f9956dd..bd5a97a41 100644 --- a/static/css/utils/asyncForm.scss +++ b/static/css/utils/asyncForm.scss @@ -1,4 +1,4 @@ -.async-form-response { +.async-form__response { margin: 20px 0; position: relative; width: 100%; @@ -7,15 +7,15 @@ padding-top: 60px; } -.async-form-response::before, -.async-form-response::after { +.async-form__response::before, +.async-form__response::after { position: absolute; top: 0px; left: 50%; display: block; } -.async-form-response--success::before { +.async-form__response--success::before { content: ''; width: 17px; height: 28px; @@ -24,7 +24,7 @@ transform: translateX(-50%) rotate(45deg); } -.async-form-response--info::before { +.async-form__response--info::before { content: ''; width: 5px; height: 30px; @@ -32,7 +32,7 @@ background-color: #777; transform: translateX(-50%); } -.async-form-response--info::after { +.async-form__response--info::after { content: ''; width: 5px; height: 5px; @@ -40,14 +40,14 @@ transform: translateX(-50%); } -.async-form-response--warning::before { +.async-form__response--warning::before { content: ''; width: 5px; height: 30px; background-color: rgb(255, 187, 0); transform: translateX(-50%); } -.async-form-response--warning::after { +.async-form__response--warning::after { content: ''; width: 5px; height: 5px; @@ -56,14 +56,14 @@ transform: translateX(-50%); } -.async-form-response--error::before { +.async-form__response--error::before { content: ''; width: 5px; height: 40px; background-color: #940d0d; transform: translateX(-50%) rotate(-45deg); } -.async-form-response--error::after { +.async-form__response--error::after { content: ''; width: 5px; height: 40px; @@ -71,7 +71,7 @@ transform: translateX(-50%) rotate(45deg); } -.async-form-loading { +.async-form--loading { opacity: 0.1; transition: opacity 800ms ease-out; pointer-events: none; diff --git a/static/js/utils/asyncForm.js b/static/js/utils/asyncForm.js index aa57ed2a0..c7fde55c5 100644 --- a/static/js/utils/asyncForm.js +++ b/static/js/utils/asyncForm.js @@ -1,33 +1,62 @@ (function collonadeClosure() { 'use strict'; - window.utils = window.utils || {}; + /** + * + * Async Form Utility + * prevents form submissions from reloading the page but instead firing an AJAX request + * + * Attribute: uw-async-form + * (works only on
elements) + * + * Example usage: + * + * ... + */ - var ASYNC_FORM_RESPONSE_CLASS = 'async-form-response'; - var ASYNC_FORM_LOADING_CLASS = 'async-form-loading'; + var ASYNC_FORM_UTIL_NAME = 'asyncForm'; + var ASYNC_FORM_UTIL_SELECTOR = 'form[uw-async-form]'; + + var ASYNC_FORM_INITIALIZED_CLASS = 'check-all--initialized'; + var ASYNC_FORM_RESPONSE_CLASS = 'async-form__response'; + var ASYNC_FORM_LOADING_CLASS = 'async-form--loading'; var ASYNC_FORM_MIN_DELAY = 600; - var DEFAULT_FAILURE_MESSAGE = 'The response we received from the server did not match what we expected. Please let us know this happened via the help widget in the top navigation.'; + var ASYNC_FORM_DEFAULT_FAILURE_MESSAGE = 'The response we received from the server did not match what we expected. Please let us know this happened via the help widget in the top navigation.'; - window.utils.asyncForm = function(formElement, options) { - - options = options || {}; + var asyncFormUtil = function(element) { var lastRequestTimestamp = 0; - function setup() { - formElement.addEventListener('submit', submitHandler); + function init() { + if (!element) { + throw new Error('Async Form Utility cannot be setup without an element!'); + } + + if (element.classList.contains(ASYNC_FORM_INITIALIZED_CLASS)) { + return false; + } + + element.addEventListener('submit', submitHandler); + + element.classList.add(ASYNC_FORM_INITIALIZED_CLASS); + + return { + name: ASYNC_FORM_UTIL_NAME, + element: element, + destroy: function() {}, + }; } function processResponse(response) { var responseElement = makeResponseElement(response.content, response.status); - var parentElement = formElement.parentElement; + var parentElement = element.parentElement; // make sure there is a delay between click and response var delay = Math.max(0, ASYNC_FORM_MIN_DELAY + lastRequestTimestamp - Date.now()); setTimeout(function() { - parentElement.insertBefore(responseElement, formElement); - formElement.remove(); + parentElement.insertBefore(responseElement, element); + element.remove(); }, delay); } @@ -43,12 +72,12 @@ function submitHandler(event) { event.preventDefault(); - formElement.classList.add(ASYNC_FORM_LOADING_CLASS) + element.classList.add(ASYNC_FORM_LOADING_CLASS) lastRequestTimestamp = Date.now(); - var url = formElement.getAttribute('action'); + var url = element.getAttribute('action'); var headers = { }; - var body = new FormData(formElement); + var body = new FormData(element); if (options && options.headers) { Object.keys(options.headers).forEach(function(headerKey) { @@ -66,21 +95,24 @@ }).then(function(response) { processResponse(response[0]); }).catch(function(error) { - var failureMessage = DEFAULT_FAILURE_MESSAGE; + var failureMessage = ASYNC_FORM_DEFAULT_FAILURE_MESSAGE; if (options.i18n && options.i18n.asyncFormFailure) { failureMessage = options.i18n.asyncFormFailure; } processResponse({ content: failureMessage }); - formElement.classList.remove(ASYNC_FORM_LOADING_CLASS); + element.classList.remove(ASYNC_FORM_LOADING_CLASS); }); } - setup(); - - return { - scope: formElement, - destroy: function() {}, - }; + return init(); }; + + if (UtilRegistry) { + UtilRegistry.register({ + name: ASYNC_FORM_UTIL_NAME, + selector: ASYNC_FORM_UTIL_SELECTOR, + setup: asyncFormUtil + }); + } })();