Merge branch 'form-errors' into 'master'

small ui/ux refinements

See merge request !170
This commit is contained in:
Felix Hamann 2019-04-01 22:54:12 +02:00
commit 6fbb1888c5
9 changed files with 90 additions and 51 deletions

View File

@ -66,6 +66,14 @@
input, textarea {
border-color: var(--color-error) !important;
}
.form-error {
display: block;
}
}
.form-error {
display: none;
}
@media (max-width: 768px) {

View File

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

View File

@ -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() {

View File

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

View File

@ -1,17 +1,14 @@
<div .container>
<h1>Uni2work - Admin Demopage
<section>
<p data-tooltip="Solch ein Tooltip kann mit dem <em>data-tooltip</em> Attribut erzeugt werden. Funktioniert aber nur in Block-Elementen die einen sinnvollen Wrapper haben.">
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.
<div .container.js-show-hide>
<section>
<h2 .js-show-hide__toggle>Teilweise funktionierende Abschnitte
<ul .js-show-hide__target>
<ul>
<li .list-group-item>
<a href=@{UsersR}>Benutzer Verwaltung
@ -22,7 +19,7 @@
<li .list-group-item>
<a href=@{CourseNewR}>Kurse anlegen
<div .container>
<section>
<h2>Funktionen zum Testen
<ul>

View File

@ -474,7 +474,7 @@ ul.list--inline {
/* DEFINITION LIST */
.deflist {
display: grid;
grid-template-columns: 100% ;
grid-template-columns: 100%;
}
.deflist__dt,
.deflist__dd {
@ -488,6 +488,10 @@ ul.list--inline {
.deflist__dd {
font-size: 18px;
margin-bottom: 10px;
> p {
margin-top: 0;
}
}
@media (min-width: 768px) {
@ -507,7 +511,6 @@ ul.list--inline {
.deflist__dt,
.deflist__dd {
border-bottom: 1px solid #d3d3d3;
padding: 12px 0;
margin: 0;
font-size: 16px;
@ -527,16 +530,14 @@ ul.list--inline {
}
section {
padding-bottom: 20px;
margin-bottom: 20px;
padding-bottom: 30px;
border-bottom: 1px solid #d3d3d3;
+ section {
margin-top: 20px;
padding-top: 20px;
}
&:last-of-type {
&:last-child {
border-bottom: none;
}
}

View File

@ -1,6 +1,7 @@
<section>
UniWorX erfahrene Veranstalter finden
hier die wichtigsten Neuerungen.
UniWorX erfahrene Veranstalter finden
hier die wichtigsten Neuerungen.
<section>
<h2>Bekannte Probleme in Bearbeitung
@ -178,4 +179,4 @@ hier die wichtigsten Neuerungen.
Planmäßige Wartungen werden ohne Ankündigung
immer um 2:00h nachts durchgeführt.
Es wird daher empfohlen, keine kritischen Abgabefristen
um oder kurz nach dieser Zeit einzustellen.
um oder kurz nach dieser Zeit einzustellen.

View File

@ -1,4 +1,3 @@
.table-filter {
border-bottom: 1px solid #d3d3d3;
margin-bottom: 13px;
}

View File

@ -1,18 +1,19 @@
$newline never
$# Wrapper for all kinds of forms
<form ##{formId} method=#{decodeUtf8 (renderStdMethod formMethod)} action=#{fromMaybe "" formActionUrl} enctype=#{formEncoding} *{formAttrs}>
$# Distinguish different falvours of submit button layouts here:
$case formSubmit
$of FormNoSubmit
^{formWidget}
$of FormSubmit
^{formWidget}
^{submitButtonView}
$of FormDualSubmit
^{submitButtonView}
^{formWidget}
^{submitButtonView}
$of FormAutoSubmit
^{formWidget}
<button type=submit data-autosubmit>
^{btnLabel BtnSubmit}
<section>
<form ##{formId} method=#{decodeUtf8 (renderStdMethod formMethod)} action=#{fromMaybe "" formActionUrl} enctype=#{formEncoding} *{formAttrs}>
$# Distinguish different falvours of submit button layouts here:
$case formSubmit
$of FormNoSubmit
^{formWidget}
$of FormSubmit
^{formWidget}
^{submitButtonView}
$of FormDualSubmit
^{submitButtonView}
^{formWidget}
^{submitButtonView}
$of FormAutoSubmit
^{formWidget}
<button type=submit data-autosubmit>
^{btnLabel BtnSubmit}