Skip to content

Commit

Permalink
Better error messages for standalone contact forms
Browse files Browse the repository at this point in the history
Refs #20
  • Loading branch information
Ugoku committed Oct 15, 2019
1 parent 9fddd76 commit 9199ab6
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 21 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## master
* Small styling improvement
* Option `showSubmit` for contact forms is now named `standalone`. The old option will remain as alias until the next major version
* Better error messages for standalone contact forms

## 1.1.1 (2019-10-03)
* Technical: Upgrade to Babel 7
Expand Down
151 changes: 140 additions & 11 deletions dist/onlinebooking.js
Original file line number Diff line number Diff line change
Expand Up @@ -1741,6 +1741,19 @@ function () {
value: function findElements(querystring) {
return this.element.querySelectorAll(querystring);
}
}, {
key: "isStandalone",
value: function isStandalone(options) {
if (options.standalone) {
return true;
}

if (options.showSubmit) {
console.warn('Option "showSubmit" was renamed to "standalone". Please update your code.');
}

return false;
}
}, {
key: "generateForm",
value: function generateForm() {
Expand All @@ -1759,7 +1772,10 @@ function () {
}

return Promise.all(waitFor).then(function () {
var html = '<form class="recras-contactform">';
var standalone = _this30.isStandalone(extraOptions);

var validateText = standalone ? 'novalidate' : '';
var html = "<form class=\"recras-contactform\" ".concat(validateText, ">");

if (extraOptions.voucherQuantitySelector) {
html += _this30.quantitySelector();
Expand All @@ -1769,7 +1785,7 @@ function () {
html += '<div>' + _this30.showField(field, idx) + '</div>';
});

if (extraOptions.showSubmit) {
if (standalone) {
html += _this30.submitButtonHtml();
}

Expand Down Expand Up @@ -1845,6 +1861,39 @@ function () {
return 0;
});
}
}, {
key: "getInvalidFields",
value: function getInvalidFields() {
var invalid = [];
var required = this.getRequiredFields();
var els = this.findElements('.recras-contactform :invalid');

for (var _i8 = 0; _i8 < els.length; _i8++) {
var el = els[_i8];

if (!required.includes(el)) {
invalid.push(el);
}
}

return invalid;
}
}, {
key: "getRequiredFields",
value: function getRequiredFields() {
var isEmpty = [];
var els = this.findElements('.recras-contactform :required');

for (var _i10 = 0; _i10 < els.length; _i10++) {
var el = els[_i10];

if (el.value === undefined || el.value === '') {
isEmpty.push(el);
}
}

return isEmpty;
}
}, {
key: "hasFieldOfType",
value: function hasFieldOfType(identifier) {
Expand All @@ -1867,6 +1916,31 @@ function () {
value: function hasPackageField() {
return this.hasFieldOfType('boeking.arrangement');
}
}, {
key: "isEmpty",
value: function isEmpty() {
var isEmpty = true;
var els = this.findElements('.recras-contactform input, .recras-contactform select, .recras-contactform textarea');

var formValues = _toConsumableArray(els).map(function (el) {
return el.value;
});

for (var _i12 = 0; _i12 < formValues.length; _i12++) {
var val = formValues[_i12];

if (val !== '') {
isEmpty = false;
}
}

return isEmpty;
}
}, {
key: "isValid",
value: function isValid() {
return this.findElement('.recras-contactform').checkValidity();
}
}, {
key: "loadingIndicatorHide",
value: function loadingIndicatorHide() {
Expand All @@ -1888,13 +1962,27 @@ function () {
value: function quantitySelector() {
return "<div><label for=\"number-of-vouchers\">".concat(this.languageHelper.translate('VOUCHER_QUANTITY'), "</label><input type=\"number\" id=\"number-of-vouchers\" class=\"number-of-vouchers\" min=\"1\" value=\"1\" required></div>");
}
}, {
key: "removeErrors",
value: function removeErrors() {
var parentQuery = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';

_toConsumableArray(this.findElements(parentQuery + ' .booking-error')).forEach(function (el) {
el.parentNode.removeChild(el);
});
}
}, {
key: "removeWarnings",
value: function removeWarnings() {
_toConsumableArray(this.findElements('.recrasError')).forEach(function (el) {
el.parentNode.removeChild(el);
});
}
}, {
key: "requiredIsEmpty",
value: function requiredIsEmpty() {
return this.getRequiredFields().length > 0;
}
}, {
key: "showField",
value: function showField(field, idx) {
Expand Down Expand Up @@ -2017,7 +2105,7 @@ function () {
this.loadingIndicatorShow(this.element);
return this.getContactFormFields().then(function () {
return _this34.generateForm({
showSubmit: true
standalone: true
});
}).then(function (html) {
_this34.appendHtml(html);
Expand All @@ -2040,6 +2128,30 @@ function () {
_this34.loadingIndicatorHide();
});
}
}, {
key: "showInlineErrors",
value: function showInlineErrors() {
for (var _i14 = 0, _this$getRequiredFiel2 = this.getRequiredFields(); _i14 < _this$getRequiredFiel2.length; _i14++) {
var el = _this$getRequiredFiel2[_i14];
var labelEl = el.parentNode.querySelector('label');
var requiredText = this.languageHelper.translate('CONTACT_FORM_FIELD_REQUIRED', {
FIELD_NAME: labelEl.innerText
});
el.parentNode.insertAdjacentHTML('afterend', "<div class=\"booking-error\">".concat(requiredText, "</div>"));
}

for (var _i16 = 0, _this$getInvalidField2 = this.getInvalidFields(); _i16 < _this$getInvalidField2.length; _i16++) {
var _el = _this$getInvalidField2[_i16];

var _labelEl = _el.parentNode.querySelector('label');

var invalidText = this.languageHelper.translate('CONTACT_FORM_FIELD_INVALID', {
FIELD_NAME: _labelEl.innerText
});

_el.parentNode.insertAdjacentHTML('afterend', "<div class=\"booking-error\">".concat(invalidText, "</div>"));
}
}
}, {
key: "showLabel",
value: function showLabel(field, idx) {
Expand All @@ -2062,14 +2174,22 @@ function () {
var _this35 = this;

e.preventDefault();
this.eventHelper.sendEvent(RecrasEventHelper.PREFIX_CONTACT_FORM, RecrasEventHelper.EVENT_CONTACT_FORM_SUBMIT, this.options.getFormId());
var submitButton = this.findElement('.submitForm');
var status = this.checkRequiredCheckboxes();
this.removeErrors('.recras-contactform');

if (!status) {
if (this.isEmpty()) {
submitButton.parentNode.insertAdjacentHTML('afterend', "<div class=\"booking-error\">".concat(this.languageHelper.translate('ERR_CONTACT_FORM_EMPTY'), "</div>"));
return false;
} else if (this.requiredIsEmpty() || !this.isValid()) {
this.showInlineErrors();
return false;
}

if (!this.checkRequiredCheckboxes()) {
return false;
}

this.eventHelper.sendEvent(RecrasEventHelper.PREFIX_CONTACT_FORM, RecrasEventHelper.EVENT_CONTACT_FORM_SUBMIT, this.options.getFormId());
this.loadingIndicatorHide();
this.loadingIndicatorShow(submitButton);
submitButton.setAttribute('disabled', 'disabled');
Expand Down Expand Up @@ -2106,12 +2226,12 @@ function () {
_createClass(RecrasCSSHelper, null, [{
key: "cssBooking",
value: function cssBooking() {
return "\n.recras-onlinebooking {\n max-width: 800px;\n}\n.recras-onlinebooking > *:not(:first-child) + * {\n border-top: 2px solid #dedede; /* Any love for Kirby out there? */\n}\n.recras-input-invalid {\n border: 1px solid hsl(0, 50%, 50%);\n}\n.booking-error, .minimum-amount {\n color: hsl(0, 50%, 50%);\n}\n.minimum-amount {\n padding-left: 0.5em;\n}\n.recras-datetime {\n display: -ms-grid;\n display: grid;\n -ms-grid-columns: 30% 70%;\n grid-template-columns: 30% 70%;\n}\n.recras-datetime > * {\n margin: 0.25em 0;\n}\n.recras-datetime label {\n display: block;\n -ms-grid-column: 1;\n}\n.recras-datetime input, .recras-datetime select {\n max-width: 12em;\n -ms-grid-column: 2;\n}\n.recras-datetime > :nth-child(-n + 2) {\n -ms-grid-row: 1;\n}\n.recras-datetime > :nth-last-child(-n + 2) {\n -ms-grid-row: 2;\n}\n.time-preview {\n padding-right: 0.5em;\n}\n.recrasUnitPrice {\n opacity: 0.5;\n}\n.bookPackage, .submitForm, .buyTemplate {\n font: inherit;\n font-weight: bold;\n padding: 0.5em 2em;\n}\n";
return "\n.recras-onlinebooking {\n max-width: 800px;\n}\n.recras-onlinebooking > *:not(:first-child) + * {\n border-top: 2px solid #dedede; /* Any love for Kirby out there? */\n}\n.recras-input-invalid {\n border: 1px solid hsl(0, 50%, 50%);\n}\n.booking-error, .minimum-amount {\n color: hsl(0, 50%, 50%);\n}\n.minimum-amount {\n padding-left: 0.5em;\n}\n.recras-datetime {\n display: -ms-grid;\n display: grid;\n -ms-grid-columns: 30% 70%;\n grid-template-columns: 30% 70%;\n}\n.recras-datetime > * {\n margin: 0.25em 0;\n}\n.recras-datetime label {\n display: block;\n -ms-grid-column: 1;\n}\n.recras-datetime input, .recras-datetime select {\n max-width: 12em;\n -ms-grid-column: 2;\n}\n.recras-datetime > :nth-child(-n + 2) {\n -ms-grid-row: 1;\n}\n.recras-datetime > :nth-last-child(-n + 2) {\n -ms-grid-row: 2;\n}\n.time-preview {\n padding-right: 0.5em;\n}\n.recrasUnitPrice {\n opacity: 0.5;\n}\n";
}
}, {
key: "cssGlobal",
value: function cssGlobal() {
return "\n.latestError, .recrasError {\n color: hsl(0, 50%, 50%);\n}\n.recras-onlinebooking > *:not(.latestError):not(.recrasLoadingIndicator) {\n padding: 1em 0;\n}\n.recras-amountsform > div {\n display: -ms-grid;\n display: grid;\n -ms-grid-columns: 1fr 5em 7em;\n grid-template-columns: 1fr 5em 7em;\n}\n.recras-datetime, .recras-discounts > div, .recras-contactform > div {\n display: -ms-grid;\n display: grid;\n -ms-grid-columns: 1fr 12em;\n grid-template-columns: 1fr 12em;\n}\n.recras-contactform > div {\n padding: 0.25em 0;\n}\n.recras-contactform label {\n display: block;\n}\n.recras-amountsform .recras-full-width {\n display: block;\n}\n\n.recrasLoadingIndicator {\n animation: recrasSpinner 1.1s infinite linear;\n border: 0.2em solid rgba(0, 0, 0, 0.2);\n border-left-color: rgba(0, 0, 0, 0.5);\n border-radius: 50%;\n display: inline-block;\n height: 2em;\n overflow: hidden;\n text-indent: -100vw;\n width: 2em;\n}\n@keyframes recrasSpinner {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\nbutton .recrasLoadingIndicator, label .recrasLoadingIndicator {\n height: 1em;\n vertical-align: middle;\n width: 1em;\n}\nbutton .recrasLoadingIndicator {\n margin-left: 0.5em;\n}\n";
return "\n.latestError, .recrasError {\n color: hsl(0, 50%, 50%);\n}\n.recras-onlinebooking > *:not(.latestError):not(.recrasLoadingIndicator) {\n padding: 1em 0;\n}\n.recras-amountsform > div {\n display: -ms-grid;\n display: grid;\n -ms-grid-columns: 1fr 5em 7em;\n grid-template-columns: 1fr 5em 7em;\n}\n.recras-datetime, .recras-discounts > div, .recras-contactform > div {\n display: -ms-grid;\n display: grid;\n -ms-grid-columns: 1fr 12em;\n grid-template-columns: 1fr 12em;\n}\n.recras-contactform > div {\n padding-bottom: 0.25em;\n padding-top: 0.25em;\n}\n.recras-contactform label {\n display: block;\n}\n.recras-amountsform .recras-full-width {\n display: block;\n}\n\n.recrasLoadingIndicator {\n animation: recrasSpinner 1.1s infinite linear;\n border: 0.2em solid rgba(0, 0, 0, 0.2);\n border-left-color: rgba(0, 0, 0, 0.5);\n border-radius: 50%;\n display: inline-block;\n height: 2em;\n overflow: hidden;\n text-indent: -100vw;\n width: 2em;\n}\n@keyframes recrasSpinner {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\nbutton .recrasLoadingIndicator, label .recrasLoadingIndicator {\n height: 1em;\n vertical-align: middle;\n width: 1em;\n}\nbutton .recrasLoadingIndicator {\n margin-left: 0.5em;\n}\n.bookPackage, .submitForm, .buyTemplate {\n font: inherit;\n font-weight: bold;\n padding: 0.5em 2em;\n}\n";
}
}, {
key: "insertIntoHead",
Expand Down Expand Up @@ -2386,14 +2506,16 @@ function () {
ATTR_REQUIRED: 'Erforderlich',
BOOKING_DISABLED_AGREEMENT: 'You have not agreed to the terms yet',
BOOKING_DISABLED_AMOUNTS_INVALID: 'Programme amounts are invalid',
BOOKING_DISABLED_CONTACT_FORM_INVALID: 'Contact form is not filled in completely, or contains invalid values',
BOOKING_DISABLED_CONTACT_FORM_INVALID: 'Contact form is not filled in correctly',
BOOKING_DISABLED_INVALID_DATE: 'No date selected',
BOOKING_DISABLED_INVALID_TIME: 'No time selected',
BOOKING_DISABLED_REQUIRED_PRODUCT: 'Required product not yet selected',
BUTTON_BOOK_NOW: 'Jetzt buchen',
BUTTON_BUY_NOW: 'Jetzt kaufen',
BUTTON_SUBMIT_CONTACT_FORM: 'Submit',
CONTACT_FORM_CHECKBOX_REQUIRED: 'At least one option must be checked',
CONTACT_FORM_FIELD_INVALID: '"{FIELD_NAME}" is invalid',
CONTACT_FORM_FIELD_REQUIRED: '"{FIELD_NAME}" is a required field',
CONTACT_FORM_SUBMIT_FAILED: 'The contact form could not be sent. Please try again later.',
CONTACT_FORM_SUBMIT_SUCCESS: 'The contact form was sent successfully.',
DATE: 'Datum',
Expand Down Expand Up @@ -2432,6 +2554,7 @@ function () {
DISCOUNT_TITLE: 'Rabattcode oder Gutschein',
DISCOUNT_INVALID: 'Ungültiger Rabattcode oder Gutschein',
ERR_AMOUNTS_NO_PACKAGE: 'Option "productAmounts" is set, but "package_id" is not set',
ERR_CONTACT_FORM_EMPTY: 'Contact form is not filled in',
ERR_GENERAL: 'Etwas ist schief gelaufen:',
ERR_INVALID_ELEMENT: 'Option "Element" ist kein gültiges Element',
ERR_INVALID_HOSTNAME: 'Option "recras_hostname" ist ungültig.',
Expand Down Expand Up @@ -2466,14 +2589,16 @@ function () {
ATTR_REQUIRED: 'Required',
BOOKING_DISABLED_AGREEMENT: 'You have not agreed to the terms yet',
BOOKING_DISABLED_AMOUNTS_INVALID: 'Programme amounts are invalid',
BOOKING_DISABLED_CONTACT_FORM_INVALID: 'Contact form is not filled in completely, or contains invalid values',
BOOKING_DISABLED_CONTACT_FORM_INVALID: 'Contact form is not filled in correctly',
BOOKING_DISABLED_INVALID_DATE: 'No date selected',
BOOKING_DISABLED_INVALID_TIME: 'No time selected',
BOOKING_DISABLED_REQUIRED_PRODUCT: 'Required product not yet selected',
BUTTON_BOOK_NOW: 'Book now',
BUTTON_BUY_NOW: 'Buy now',
BUTTON_SUBMIT_CONTACT_FORM: 'Submit',
CONTACT_FORM_CHECKBOX_REQUIRED: 'At least one option must be checked',
CONTACT_FORM_FIELD_INVALID: '"{FIELD_NAME}" is invalid',
CONTACT_FORM_FIELD_REQUIRED: '"{FIELD_NAME}" is a required field',
CONTACT_FORM_SUBMIT_FAILED: 'The contact form could not be sent. Please try again later.',
CONTACT_FORM_SUBMIT_SUCCESS: 'The contact form was sent successfully.',
DATE: 'Date',
Expand Down Expand Up @@ -2512,6 +2637,7 @@ function () {
DISCOUNT_TITLE: 'Discount code or voucher',
DISCOUNT_INVALID: 'Invalid discount code or voucher',
ERR_AMOUNTS_NO_PACKAGE: 'Option "productAmounts" is set, but "package_id" is not set',
ERR_CONTACT_FORM_EMPTY: 'Contact form is not filled in',
ERR_GENERAL: 'Something went wrong:',
ERR_INVALID_ELEMENT: 'Option "element" is not a valid Element',
ERR_INVALID_HOSTNAME: 'Option "recras_hostname" is invalid.',
Expand Down Expand Up @@ -2546,14 +2672,16 @@ function () {
ATTR_REQUIRED: 'Vereist',
BOOKING_DISABLED_AGREEMENT: 'Je bent nog niet akkoord met de voorwaarden',
BOOKING_DISABLED_AMOUNTS_INVALID: 'Aantallen in programma zijn ongeldig',
BOOKING_DISABLED_CONTACT_FORM_INVALID: 'Contactformulier is niet volledig ingevuld, of bevat ongeldige waardes',
BOOKING_DISABLED_CONTACT_FORM_INVALID: 'Contactformulier is niet correct ingevuld',
BOOKING_DISABLED_INVALID_DATE: 'Geen datum geselecteerd',
BOOKING_DISABLED_INVALID_TIME: 'Geen tijd geselecteerd',
BOOKING_DISABLED_REQUIRED_PRODUCT: 'Vereist product nog niet geselecteerd',
BUTTON_BOOK_NOW: 'Nu boeken',
BUTTON_BUY_NOW: 'Nu kopen',
BUTTON_SUBMIT_CONTACT_FORM: 'Versturen',
CONTACT_FORM_CHECKBOX_REQUIRED: 'Ten minste één optie moet aangevinkt worden',
CONTACT_FORM_FIELD_INVALID: '"{FIELD_NAME}" is ongeldig',
CONTACT_FORM_FIELD_REQUIRED: '"{FIELD_NAME}" is een verplicht veld',
CONTACT_FORM_SUBMIT_FAILED: 'Het contactformulier kon niet worden verstuurd. Probeer het later nog eens.',
CONTACT_FORM_SUBMIT_SUCCESS: 'Het contactformulier is succesvol verstuurd.',
DATE: 'Datum',
Expand Down Expand Up @@ -2592,6 +2720,7 @@ function () {
DISCOUNT_TITLE: 'Kortingscode of tegoedbon',
DISCOUNT_INVALID: 'Ongeldige kortingscode of tegoedbon',
ERR_AMOUNTS_NO_PACKAGE: 'Optie "productAmounts" is ingesteld, maar "package_id" is niet ingesteld',
ERR_CONTACT_FORM_EMPTY: 'Contactformulier is niet ingevuld',
ERR_GENERAL: 'Er ging iets mis:',
ERR_INVALID_ELEMENT: 'Optie "element" is geen geldig Element',
ERR_INVALID_HOSTNAME: 'Optie "recras_hostname" is ongeldig.',
Expand Down
5 changes: 5 additions & 0 deletions docs/demo.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ input[type="number"] {
color: #a00;
padding-left: 0.25em;
}
.booking-error {
background: hsl(0, 25%, 96%);
border: 1px solid #a00;
padding: 0.5em;
}

.recras-onlinebooking > *:not(:first-child) + * {
border-top: 2px solid hsla(147, 25%, 25%, 0.25);
Expand Down
Loading

0 comments on commit 9199ab6

Please sign in to comment.