diff --git a/static/css/utils/inputs.scss b/static/css/utils/inputs.scss index 6bf5286e3..db16a94d3 100644 --- a/static/css/utils/inputs.scss +++ b/static/css/utils/inputs.scss @@ -66,6 +66,14 @@ input, textarea { border-color: var(--color-error) !important; } + + .form-error { + display: block; + } +} + +.form-error { + display: none; } @media (max-width: 768px) { diff --git a/static/js/utils/form.js b/static/js/utils/form.js index 2e9bbc92b..77437a3e1 100644 --- a/static/js/utils/form.js +++ b/static/js/utils/form.js @@ -8,6 +8,9 @@ var AUTOSUBMIT_BUTTON_SELECTOR = '[type="submit"][data-autosubmit]'; var AJAX_SUBMIT_FLAG = 'ajaxSubmit'; + var FORM_GROUP_CLASS = 'form-group'; + var FORM_GROUP_WITH_ERRORS_CLASS = 'form-group--has-error'; + function formValidator(inputs) { var done = true; inputs.forEach(function(inp) { @@ -52,6 +55,12 @@ // inputs utilInstances.push(window.utils.setup('inputs', form, options)); + // form group errors + var formGroups = Array.from(form.querySelectorAll('.' + FORM_GROUP_CLASS)); + formGroups.forEach(function(formGroup) { + utilInstances.push(window.utils.setup('errorRemover', formGroup, options)); + }); + form.classList.add(JS_INITIALIZED); function destroyUtils() { @@ -165,4 +174,29 @@ destroy: function() {}, }; }; + + // listens for focus events and removes any errors on an input + window.utils.errorRemover = function(formGroup, options) { + + var inputElement = formGroup.querySelector('input:not([type="hidden"]), textarea, select'); + if (!inputElement) { + return false; + } + + inputElement.addEventListener('focus', focusListener); + + function focusListener() { + var hasError = formGroup.classList.contains(FORM_GROUP_WITH_ERRORS_CLASS); + if (hasError) { + formGroup.classList.remove(FORM_GROUP_WITH_ERRORS_CLASS); + } + } + + return { + scope: formGroup, + destroy: function() { + inputElement.removeEventListener('focus', focusListener); + }, + }; + }; })(); diff --git a/static/js/utils/inputs.js b/static/js/utils/inputs.js index fd4ad906e..3a9f7ebc4 100644 --- a/static/js/utils/inputs.js +++ b/static/js/utils/inputs.js @@ -3,37 +3,37 @@ window.utils = window.utils || {}; - var JS_INITIALIZED_CLASS = 'js-initialized'; - - function isNotInitialized(element) { - return !element.classList.contains(JS_INITIALIZED_CLASS); - } + var JS_INITIALIZED_CLASS = 'js-inputs-initialized'; window.utils.inputs = function(wrapper, options) { - + options = options || {}; var utilInstances = []; + if (wrapper.classList.contains(JS_INITIALIZED_CLASS) && !options.force) { + return false; + } + // checkboxes var checkboxes = Array.from(wrapper.querySelectorAll('input[type="checkbox"]')); - checkboxes.filter(isNotInitialized).forEach(function(checkbox) { + checkboxes.forEach(function(checkbox) { utilInstances.push(window.utils.setup('checkbox', checkbox)); }); // radios var radios = Array.from(wrapper.querySelectorAll('input[type="radio"]')); - radios.filter(isNotInitialized).forEach(function(radio) { + radios.forEach(function(radio) { utilInstances.push(window.utils.setup('radio', radio)); }); // file-uploads var fileUploads = Array.from(wrapper.querySelectorAll('input[type="file"]')); - fileUploads.filter(isNotInitialized).forEach(function(input) { + fileUploads.forEach(function(input) { utilInstances.push(window.utils.setup('fileUpload', input, options)); }); // file-checkboxes var fileCheckboxes = Array.from(wrapper.querySelectorAll('.file-checkbox')); - fileCheckboxes.filter(isNotInitialized).forEach(function(input) { + fileCheckboxes.forEach(function(input) { utilInstances.push(window.utils.setup('fileCheckbox', input, options)); }); @@ -45,6 +45,8 @@ }); } + wrapper.classList.add(JS_INITIALIZED_CLASS); + return { scope: wrapper, destroy: destroyUtils, @@ -74,7 +76,6 @@ if (!i18n) { throw new Error('window.utils.fileUpload(input, options) needs to be passed i18n object via options'); } - input.classList.add(JS_INITIALIZED_CLASS); function renderFileList(files) { fileList.innerHTML = ''; @@ -166,8 +167,6 @@ cont = cont.parentNode; } addListener(cont); - input.classList.add(JS_INITIALIZED_CLASS); - cont.classList.add(JS_INITIALIZED_CLASS); } setup(); @@ -190,7 +189,6 @@ labelEl.setAttribute('for', input.id); wrapperEl.appendChild(input); wrapperEl.appendChild(labelEl); - input.classList.add(JS_INITIALIZED_CLASS); if (siblingEl) { parentEl.insertBefore(wrapperEl, siblingEl); @@ -219,7 +217,6 @@ wrapperEl.appendChild(siblingEl); } - input.classList.add(JS_INITIALIZED_CLASS); parentEl.appendChild(wrapperEl); } @@ -233,8 +230,6 @@ window.utils.implicitSubmit = function(input, options) { var submit = options.submit; - console.log('implicitSubmit', input, submit); - if (!submit) { throw new Error('window.utils.implicitSubmit(input, options) needs to be passed a submit element via options'); } @@ -247,7 +242,7 @@ }; input.addEventListener('keypress', doSubmit); - + return { scope: input, destroy: function() { diff --git a/static/js/utils/setup.js b/static/js/utils/setup.js index e9afb216b..bb3bb0e3d 100644 --- a/static/js/utils/setup.js +++ b/static/js/utils/setup.js @@ -37,6 +37,9 @@ if (isAlreadySetup) { console.warn('Trying to setup a JS utility that\'s already been set up', { utility: utilName, scope, options }); + if (!options.force) { + return false; + } } } diff --git a/templates/adminTest.hamlet b/templates/adminTest.hamlet index 75622355e..7e59d9599 100644 --- a/templates/adminTest.hamlet +++ b/templates/adminTest.hamlet @@ -1,17 +1,14 @@ -
Diese interne Seite dient lediglich zum Testen diverser Funktionalitäten und zur Demonstration der verschiedenen Hilfsfunktionen/Module. Der Handler sollte jeweils aktuelle Beispiele für alle möglichen Funktionalitäten enthalten, so dass man immer weiß, wo man nachschlagen kann. - -