hr-design-system-handlebars 1.108.0 → 1.108.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.js +1 -0
- package/CHANGELOG.md +12 -0
- package/dist/assets/index.css +3 -3
- package/dist/assets/js/alpine.js +2 -1
- package/dist/assets/js/components/forms/contactForm.alpine.js +181 -18
- package/dist/assets/js/components/forms/inputHandler.alpine.js +52 -5
- package/dist/views/components/forms/controls.hbs +3 -2
- package/dist/views/components/forms/input.hbs +4 -0
- package/dist/views/components/forms/webform.hbs +37 -29
- package/dist/views_static/components/forms/controls.hbs +3 -2
- package/dist/views_static/components/forms/input.hbs +4 -0
- package/dist/views_static/components/forms/webform.hbs +37 -29
- package/package.json +2 -1
- package/src/assets/fixtures/content/copytext/copytext_webform.json +5 -58
- package/src/assets/js/alpine.js +2 -1
- package/src/stories/views/components/content/copytext/copytext-form.stories.js +41 -0
- package/src/stories/views/components/content/copytext/copytext.stories.js +0 -7
- package/src/stories/views/components/content/copytext/fixtures/copytext_webform.json +1 -1
- package/src/stories/views/components/forms/contactForm.alpine.js +181 -18
- package/src/stories/views/components/forms/controls.hbs +3 -2
- package/src/stories/views/components/forms/input.hbs +4 -0
- package/src/stories/views/components/forms/inputHandler.alpine.js +52 -5
- package/src/stories/views/components/forms/webform.hbs +37 -29
package/.storybook/main.js
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# v1.108.1 (Fri Oct 11 2024)
|
|
2
|
+
|
|
3
|
+
#### 🐛 Bug Fix
|
|
4
|
+
|
|
5
|
+
- Dpe 3384 server errors [#1100](https://github.com/mumprod/hr-design-system-handlebars/pull/1100) ([@vascoeduardo](https://github.com/vascoeduardo))
|
|
6
|
+
|
|
7
|
+
#### Authors: 1
|
|
8
|
+
|
|
9
|
+
- Vasco ([@vascoeduardo](https://github.com/vascoeduardo))
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
1
13
|
# v1.108.0 (Fri Oct 11 2024)
|
|
2
14
|
|
|
3
15
|
#### 🚀 Enhancement
|
package/dist/assets/index.css
CHANGED
|
@@ -3516,7 +3516,7 @@ article.indexTextDS .indexTextHighlighted .link {
|
|
|
3516
3516
|
border-bottom-color: var(--color-secondary-ds);
|
|
3517
3517
|
}
|
|
3518
3518
|
.counter-reset {
|
|
3519
|
-
counter-reset:
|
|
3519
|
+
counter-reset: cnt1728647677353;
|
|
3520
3520
|
}
|
|
3521
3521
|
.placeholder-text-xs::-webkit-input-placeholder {
|
|
3522
3522
|
font-size: 0.75rem;
|
|
@@ -3920,7 +3920,7 @@ article.indexTextDS .indexTextHighlighted .link {
|
|
|
3920
3920
|
--tw-ring-color: rgba(255, 255, 255, 0.5);
|
|
3921
3921
|
}
|
|
3922
3922
|
.-ordered {
|
|
3923
|
-
counter-increment:
|
|
3923
|
+
counter-increment: cnt1728647677353 1;
|
|
3924
3924
|
}
|
|
3925
3925
|
.-ordered::before {
|
|
3926
3926
|
position: absolute;
|
|
@@ -3936,7 +3936,7 @@ article.indexTextDS .indexTextHighlighted .link {
|
|
|
3936
3936
|
letter-spacing: .0125em;
|
|
3937
3937
|
--tw-text-opacity: 1;
|
|
3938
3938
|
color: rgba(0, 0, 0, var(--tw-text-opacity));
|
|
3939
|
-
content: counter(
|
|
3939
|
+
content: counter(cnt1728647677353);
|
|
3940
3940
|
}
|
|
3941
3941
|
/*! ****************************/
|
|
3942
3942
|
/*! DataPolicy stuff */
|
package/dist/assets/js/alpine.js
CHANGED
|
@@ -53,7 +53,8 @@ Alpine.store('sharingBottomPos', {
|
|
|
53
53
|
current: '0'
|
|
54
54
|
})
|
|
55
55
|
Alpine.store('forms', {
|
|
56
|
-
submissionAttempted: []
|
|
56
|
+
submissionAttempted: [],
|
|
57
|
+
serverErrorFields: []
|
|
57
58
|
})
|
|
58
59
|
// Initialization of data handlers
|
|
59
60
|
Alpine.data('mainNavigationHandler', mainNavigationHandler)
|
|
@@ -1,36 +1,199 @@
|
|
|
1
|
-
export default function contactForm(formId) {
|
|
1
|
+
export default function contactForm(formId, jsonURL, errorMessages, multipart, trackingInformations, jsonp = false) {
|
|
2
2
|
return {
|
|
3
|
+
isPosting: false,
|
|
4
|
+
isWebview:false,
|
|
5
|
+
ajaxTimeout: 60 * 1000,
|
|
6
|
+
form: this.$refs[formId],
|
|
7
|
+
formWrapper: this.$refs[formId].closest("#formWrapper"),
|
|
8
|
+
actionUrl: this.form && this.form.getAttribute('action'),
|
|
9
|
+
checkForJsonURL () {
|
|
10
|
+
if (jsonURL) {
|
|
11
|
+
this.actionUrl = jsonURL
|
|
12
|
+
}
|
|
13
|
+
},
|
|
3
14
|
formInit(){
|
|
4
|
-
this
|
|
5
|
-
console.log("
|
|
15
|
+
this.checkForJsonURL()
|
|
16
|
+
console.log("%cformId:", 'color: green' ,formId);
|
|
17
|
+
console.log("%cform:", 'color: green' ,this.form);
|
|
18
|
+
console.log("%cformWrapper:", 'color: green' ,this.formWrapper);
|
|
19
|
+
console.log("%cactionUrl:", 'color: green' ,this.actionUrl);
|
|
20
|
+
console.log("%cjsonURL:", 'color: green', jsonURL);
|
|
21
|
+
console.log("%cerrorMessages:", 'color: green', errorMessages);
|
|
22
|
+
console.log("%cmultipart:", 'color: green', multipart);
|
|
23
|
+
console.log("%ctrackingInformations:", 'color: green', trackingInformations);
|
|
24
|
+
|
|
25
|
+
this.$store.forms.submissionAttempted[formId] = false;
|
|
6
26
|
},
|
|
7
|
-
clickHandler() {
|
|
8
|
-
console.log(
|
|
9
|
-
|
|
10
|
-
console.log('form',form);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if(form.reportValidity()){
|
|
17
|
-
this.submitData();
|
|
27
|
+
clickHandler(event) {
|
|
28
|
+
console.log("event:",event);
|
|
29
|
+
console.log('check for Error:',formId);
|
|
30
|
+
console.log('form:',this.form);
|
|
31
|
+
|
|
32
|
+
if(this.form.reportValidity()){
|
|
33
|
+
// this.logData(event,form);
|
|
34
|
+
this.handleSubmit(event,this.form)
|
|
18
35
|
} else {
|
|
19
36
|
this.$store.forms.submissionAttempted[formId] = true;
|
|
20
37
|
}
|
|
21
38
|
},
|
|
22
|
-
|
|
23
|
-
|
|
39
|
+
logData(event,form) {
|
|
40
|
+
// TODO - FOR DEBUGGIN CN BE REMOVED AT THE END
|
|
41
|
+
const formData = new FormData(form);
|
|
42
|
+
const fields = Array.from(form.elements);
|
|
43
|
+
// Log the serialized form data
|
|
44
|
+
console.log(fields);
|
|
45
|
+
|
|
24
46
|
// Convert the FormData to a serialized string
|
|
25
47
|
const serializedData = Array.from(formData.entries())
|
|
26
48
|
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
27
49
|
.join('&');
|
|
28
50
|
|
|
29
51
|
// Log the serialized form data
|
|
30
|
-
console.log('DATA: ' + serializedData);
|
|
52
|
+
console.log('serialized DATA: ' + serializedData);
|
|
53
|
+
console.log('DATA:', new URLSearchParams(new FormData(form)).toString());
|
|
54
|
+
|
|
55
|
+
},
|
|
56
|
+
// TODO - Validation error handler (adapt to your case)
|
|
57
|
+
handleValidationErrors(errors) {
|
|
58
|
+
console.log('Validation Errors:', errors);
|
|
59
|
+
this.$store.forms.serverErrorFields[formId] = errors;
|
|
60
|
+
console.log('Validation Errors in Store:', this.$store.forms.serverErrorFields);
|
|
61
|
+
},
|
|
62
|
+
resetValidationErrors() {
|
|
63
|
+
this.$store.forms.serverErrorFields[formId] = {}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
handleSubmit(event, form ) {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
|
|
69
|
+
if (!this.isWebview) {
|
|
70
|
+
//uxAction(trackingInformations); // Assuming this is a tracking library
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.isPosting) return;
|
|
74
|
+
this.isPosting = true;
|
|
75
|
+
|
|
76
|
+
// TODO mit alpine umsetzen
|
|
77
|
+
//const preloadIcon = formWrapper.querySelector('.js-preloadIcon');
|
|
78
|
+
//const loadingIcon = formWrapper.querySelector('.js-loadingIcon');
|
|
79
|
+
|
|
80
|
+
// Show loading indicator
|
|
81
|
+
//preloadIcon.classList.add('-isHidden');
|
|
82
|
+
//loadingIcon.classList.remove('-isHidden');
|
|
83
|
+
|
|
84
|
+
console.log('DATA:', new URLSearchParams(new FormData(form)).toString());
|
|
85
|
+
|
|
86
|
+
// Define ajaxOptions based on form type (without jQuery $.ajax)
|
|
87
|
+
let ajaxOptions = {
|
|
88
|
+
method: jsonp ? 'GET' : 'POST',
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
|
91
|
+
},
|
|
92
|
+
timeout: this.ajaxTimeout,
|
|
93
|
+
body: new URLSearchParams(new FormData(form)),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (jsonp) {
|
|
97
|
+
ajaxOptions.url = this.actionUrl;
|
|
98
|
+
ajaxOptions.headers['Content-Type'] = 'application/json; charset=utf-8';
|
|
99
|
+
ajaxOptions.body = new URLSearchParams(new FormData(form)).toString(); // For JSONP case
|
|
100
|
+
} else if (multipart) {
|
|
101
|
+
ajaxOptions = {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
url: this.actionUrl,
|
|
104
|
+
timeout: 600 * 1000,
|
|
105
|
+
body: new FormData(form), // No URLSearchParams, we use FormData for multipart
|
|
106
|
+
processData: false,
|
|
107
|
+
headers: {}
|
|
108
|
+
};
|
|
109
|
+
} else {
|
|
110
|
+
ajaxOptions.url = `${this.actionUrl}?${responseFormatParam}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Using fetch API instead of $.ajax
|
|
114
|
+
fetch(ajaxOptions.url, ajaxOptions)
|
|
115
|
+
.then(async (response) => {
|
|
116
|
+
const data = await response.text(); // Assuming the rponse is text or JSON
|
|
117
|
+
if (response.ok) {
|
|
118
|
+
console.log('Done');
|
|
119
|
+
console.log(data);
|
|
120
|
+
if (jsonURL) {
|
|
121
|
+
const responseData = JSON.parse(data);
|
|
122
|
+
switch (responseData.status) {
|
|
123
|
+
case 'VALIDATION_ERROR':
|
|
124
|
+
this.resetValidationErrors();
|
|
125
|
+
this.handleValidationErrors(responseData.errors);
|
|
126
|
+
break;
|
|
127
|
+
case 'OK':
|
|
128
|
+
console.log("OK");
|
|
129
|
+
this.replaceAnimated(
|
|
130
|
+
this.formWrapper,
|
|
131
|
+
this.form.querySelector('#successMessage').innerHTML,
|
|
132
|
+
true
|
|
133
|
+
);
|
|
134
|
+
break;
|
|
135
|
+
default:
|
|
136
|
+
console.log("default");
|
|
137
|
+
replaceAnimated(
|
|
138
|
+
this.formWrapper,
|
|
139
|
+
this.form.querySelector('#errorMessage').innerHTML,
|
|
140
|
+
true
|
|
141
|
+
);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
replaceAnimated(this.formWrapper, data, true);
|
|
146
|
+
}
|
|
147
|
+
/*
|
|
148
|
+
if (eventOnSuccess) {
|
|
149
|
+
fireEvent(eventOnSuccess, true);
|
|
150
|
+
} */
|
|
151
|
+
|
|
152
|
+
/* if (rootElement.id) {
|
|
153
|
+
window.location.hash = rootElement.id;
|
|
154
|
+
} */
|
|
155
|
+
} else {
|
|
156
|
+
throw new Error('Network response was not ok.');
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
.catch((error) => {
|
|
160
|
+
console.error('Fail:', error);
|
|
161
|
+
replaceAnimated(
|
|
162
|
+
this.formWrapper,
|
|
163
|
+
'<div class="c-form success">Das hat leider nicht funktioniert!</div>',
|
|
164
|
+
true
|
|
165
|
+
);
|
|
166
|
+
})
|
|
167
|
+
.finally(() => {
|
|
168
|
+
console.log('Always');
|
|
169
|
+
// TODO MIT ALPINE UMSETZEN
|
|
170
|
+
//preloadIcon.classList.remove('-isHidden');
|
|
171
|
+
//loadingIcon.classList.add('-isHidden');
|
|
172
|
+
this.isPosting = false;
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
// Helper function to replace content with animation (replacing hrQuery's replaceAnimated)
|
|
176
|
+
replaceAnimated(wrapper, newContent, withFade = true) {
|
|
177
|
+
if (withFade) {
|
|
178
|
+
wrapper.style.opacity = 0;
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
wrapper.innerHTML = newContent;
|
|
181
|
+
wrapper.style.opacity = 1;
|
|
182
|
+
}, 300);
|
|
183
|
+
} else {
|
|
184
|
+
wrapper.innerHTML = newContent;
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
// Fire event utility (could be Alpine.js specific or custom)
|
|
188
|
+
fireEvent(eventName, success){
|
|
189
|
+
const event = new CustomEvent(eventName, { detail: success });
|
|
190
|
+
document.dispatchEvent(event);
|
|
31
191
|
},
|
|
32
192
|
getSubmissionAttempted() {
|
|
33
193
|
return this.$store.forms.submissionAttempted[formId]
|
|
34
194
|
}
|
|
195
|
+
|
|
35
196
|
}
|
|
36
|
-
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default function inputHandler(element, formId, errorMandatory, type, errorEmail, prefilledText = '') {
|
|
2
2
|
return {
|
|
3
3
|
[element]: prefilledText,
|
|
4
|
+
name: document.getElementById(element).getAttribute("name"),
|
|
4
5
|
valid: false,
|
|
5
6
|
wasFocused: false,
|
|
6
7
|
isFocused: false,
|
|
@@ -13,19 +14,23 @@ export default function inputHandler(element, formId, errorMandatory, type, erro
|
|
|
13
14
|
return this.valueMissing ? errorMandatory : errorEmail
|
|
14
15
|
}
|
|
15
16
|
else {
|
|
16
|
-
|
|
17
|
+
if(this.hasServerError()){
|
|
18
|
+
return this.getServerError()
|
|
19
|
+
} else {
|
|
20
|
+
return errorMandatory
|
|
21
|
+
}
|
|
17
22
|
}
|
|
18
23
|
},
|
|
19
24
|
hideDescription() {
|
|
20
25
|
switch (type) {
|
|
21
26
|
case "email":
|
|
22
|
-
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (this.typeMismatch && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]));
|
|
27
|
+
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (this.typeMismatch && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ));
|
|
23
28
|
case "checkbox":
|
|
24
|
-
return Boolean(!this.valid && this.wasFocused && !this.isFocused && !this.isChecked || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]));
|
|
29
|
+
return Boolean(!this.valid && this.wasFocused && !this.isFocused && !this.isChecked || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ));
|
|
25
30
|
case "select":
|
|
26
|
-
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]))
|
|
31
|
+
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ))
|
|
27
32
|
default:
|
|
28
|
-
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]));
|
|
33
|
+
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ));
|
|
29
34
|
}
|
|
30
35
|
},
|
|
31
36
|
hideError() {
|
|
@@ -36,6 +41,48 @@ export default function inputHandler(element, formId, errorMandatory, type, erro
|
|
|
36
41
|
this.typeMismatch = field.validity.typeMismatch;
|
|
37
42
|
this.valueMissing = field.validity.valueMissing;
|
|
38
43
|
this.valid = field.checkValidity()
|
|
44
|
+
},
|
|
45
|
+
hasServerError() {
|
|
46
|
+
return Boolean(this.$store.forms.serverErrorFields[formId][this.name])
|
|
47
|
+
},
|
|
48
|
+
getServerError() {
|
|
49
|
+
let serverError = "Server Error: "
|
|
50
|
+
switch (this.$store.forms.serverErrorFields[formId][this.name]) {
|
|
51
|
+
|
|
52
|
+
case 'form_error_required':
|
|
53
|
+
serverError += "Pflichtfeld"
|
|
54
|
+
break
|
|
55
|
+
case 'form_error_max':
|
|
56
|
+
serverError += "Zu viele Zeichen"
|
|
57
|
+
break
|
|
58
|
+
case 'form_error_validurl':
|
|
59
|
+
serverError += "Ungültige URL"
|
|
60
|
+
break
|
|
61
|
+
case 'form_error_empty':
|
|
62
|
+
serverError += "Darf nicht ausgefüllt werden"
|
|
63
|
+
break
|
|
64
|
+
case 'form_error_constants_or_null':
|
|
65
|
+
serverError += "Ungültiger Wert"
|
|
66
|
+
break
|
|
67
|
+
case 'form_error_constants':
|
|
68
|
+
serverError += "Ungültiger Wert"
|
|
69
|
+
break
|
|
70
|
+
case 'form_error_max_multivalue':
|
|
71
|
+
serverError += "Die maximale Anzahl an Antwortmöglichkeiten wurde überschritten"
|
|
72
|
+
break
|
|
73
|
+
case 'vote_error_identity_already_used':
|
|
74
|
+
serverError += "Unter dieser E-Mail-Adresse wurde bereits abgestimmt. Eine weitere Abstimmung ist nicht möglich."
|
|
75
|
+
break
|
|
76
|
+
case 'vote_error_token_request_count_exceeded':
|
|
77
|
+
serverError += "Die maximale Anzahl an Bestätigungs-E-Mails wurde bereits verschickt."
|
|
78
|
+
break
|
|
79
|
+
case 'form_error_email':
|
|
80
|
+
serverError += "Ungültige E-Mail-Adresse"
|
|
81
|
+
break
|
|
82
|
+
default:
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
return serverError
|
|
39
86
|
}
|
|
40
87
|
};
|
|
41
88
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
<div class="text-xs text-gray-500 font-headingSerif">Pflichtfeld*</div>
|
|
3
3
|
<div class="flex items-center">
|
|
4
4
|
<label class="order-2 cursor-pointer {{> components/button/utilities/button_base_classes}} {{> components/button/utilities/button_variation_classes _variant='primary'}} {{> components/button/utilities/button_dimension_classes _size='lg'}}">
|
|
5
|
-
{{> components/base/image/icon _icon="
|
|
6
|
-
<
|
|
5
|
+
<span class="hidden" :class="{'hidden': !isPosting}">{{> components/base/image/icon _icon="reload" _addClass="w-5 h-5 fill-white dark:fill-text-dark animate-spin"}}</span>
|
|
6
|
+
<span class="" :class="{'hidden': isPosting}">{{> components/base/image/icon _icon="send-ds" _addClass="w-5 h-5 fill-white dark:fill-text-dark "}}</span>
|
|
7
|
+
<input type="submit" class="pl-2 cursor-pointer" value="Absenden" @click.prevent="clickHandler($event)"/>
|
|
7
8
|
</label>
|
|
8
9
|
|
|
9
10
|
{{#> components/button/button _variant="tertiary"_size="lg" _css="order-1 mr-4" _type="reset"}}
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
DEBUG
|
|
78
78
|
</div>
|
|
79
79
|
<div class="px-4 py-3 text-red-700 bg-red-100 border border-t-0 border-red-400 rounded-b">
|
|
80
|
+
<div>name:<span x-text="name" class="font-bold" ></span></div>
|
|
80
81
|
<div>isFocused:<span x-text="isFocused" class="font-bold" :class="isFocused ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
81
82
|
<div>wasFocused:<span x-text="wasFocused" class="font-bold" :class="wasFocused ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
82
83
|
<div>valid:<span x-text="valid" class="font-bold" :class="valid ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
@@ -88,6 +89,9 @@
|
|
|
88
89
|
<div>errorMessage:<span x-text="errorMessage" class="font-bold" ></span></div>
|
|
89
90
|
<div>valueMissing:<span x-text="valueMissing" class="font-bold" ></span></div>
|
|
90
91
|
<div>typeMismatch:<span x-text="typeMismatch" class="font-bold" ></span></div>
|
|
92
|
+
<div>serverErrorFields[form{{getRandom}}][name]:<span x-text="getServerErrorFields('{{_name}}')" class="font-bold" ></span></div>
|
|
93
|
+
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
94
|
+
|
|
91
95
|
</div>
|
|
92
96
|
</div>
|
|
93
97
|
</div>
|
|
@@ -5,38 +5,46 @@
|
|
|
5
5
|
<h3 class="mb-6 text-2xl font-headingSerif sm:mb-12">
|
|
6
6
|
{{this.title}}
|
|
7
7
|
</h3>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
<div id="formWrapper">
|
|
9
|
+
<form
|
|
10
|
+
x-ref="form{{nextRandom}}"
|
|
11
|
+
ax-load
|
|
12
|
+
x-data="contactForm('form{{getRandom}}','{{this.jsonURL}}','{{this.errorMessages}}','{{this.isMultipart}}','{{this.trackingInformations}}')"
|
|
13
|
+
x-init="formInit()"
|
|
14
|
+
x-ignore
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
@submit.prevent="submitData"
|
|
17
|
+
id="form{{getRandom}}"
|
|
18
|
+
class="relative flex flex-col justify-center overflow-hidden group"
|
|
19
|
+
action="{{this.url}}"
|
|
20
|
+
method="post"
|
|
21
|
+
enctype="{{if this.isMultipart 'multipart/form-data' 'application/x-www-form-urlencoded'}}"
|
|
22
|
+
accept-charset="utf-8"
|
|
23
|
+
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
<div class="">
|
|
27
|
+
<div class="px-4 py-2 font-bold text-white bg-red-500 rounded-t">
|
|
28
|
+
DEBUG
|
|
29
|
+
</div>
|
|
30
|
+
<div class="px-4 py-3 text-red-700 bg-red-100 border border-t-0 border-red-400 rounded-b">
|
|
31
|
+
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
32
|
+
<div>isPosting:<span x-text="isPosting" class="font-bold" :class="isPosting ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
33
|
+
|
|
34
|
+
</div>
|
|
29
35
|
</div>
|
|
30
|
-
|
|
31
|
-
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
{{> components/forms/fields _formId=(joinStrings 'form' (getRandom)) }}
|
|
36
|
+
{{> components/forms/fields _formId=(joinStrings 'form' (getRandom)) }}
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
{{> components/forms/controls }}
|
|
39
|
+
<template id="successMessage">
|
|
40
|
+
<h2>SUPER DAS HAT ALLES FUNKTIONIERT</h2>
|
|
41
|
+
</template>
|
|
42
|
+
<template id="errorMessage">
|
|
43
|
+
<h2>DAS HAT LEIDER NICHT FUNKTIONIERT</h2>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
</form>
|
|
47
|
+
</div>
|
|
40
48
|
{{/components/forms/backgroundBox }}
|
|
41
49
|
{{~else~}}
|
|
42
50
|
{{> content/webform/components/webform _addClass="print:hidden copytext__clearBox marginTrailer--m"}}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
<div class="text-xs text-gray-500 font-headingSerif">Pflichtfeld*</div>
|
|
3
3
|
<div class="flex items-center">
|
|
4
4
|
<label class="order-2 cursor-pointer {{> components/button/utilities/button_base_classes}} {{> components/button/utilities/button_variation_classes _variant='primary'}} {{> components/button/utilities/button_dimension_classes _size='lg'}}">
|
|
5
|
-
{{> components/base/image/icon _icon="
|
|
6
|
-
<
|
|
5
|
+
<span class="hidden" :class="{'hidden': !isPosting}">{{> components/base/image/icon _icon="reload" _addClass="w-5 h-5 fill-white dark:fill-text-dark animate-spin"}}</span>
|
|
6
|
+
<span class="" :class="{'hidden': isPosting}">{{> components/base/image/icon _icon="send-ds" _addClass="w-5 h-5 fill-white dark:fill-text-dark "}}</span>
|
|
7
|
+
<input type="submit" class="pl-2 cursor-pointer" value="Absenden" @click.prevent="clickHandler($event)"/>
|
|
7
8
|
</label>
|
|
8
9
|
|
|
9
10
|
{{#> components/button/button _variant="tertiary"_size="lg" _css="order-1 mr-4" _type="reset"}}
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
DEBUG
|
|
78
78
|
</div>
|
|
79
79
|
<div class="px-4 py-3 text-red-700 bg-red-100 border border-t-0 border-red-400 rounded-b">
|
|
80
|
+
<div>name:<span x-text="name" class="font-bold" ></span></div>
|
|
80
81
|
<div>isFocused:<span x-text="isFocused" class="font-bold" :class="isFocused ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
81
82
|
<div>wasFocused:<span x-text="wasFocused" class="font-bold" :class="wasFocused ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
82
83
|
<div>valid:<span x-text="valid" class="font-bold" :class="valid ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
@@ -88,6 +89,9 @@
|
|
|
88
89
|
<div>errorMessage:<span x-text="errorMessage" class="font-bold" ></span></div>
|
|
89
90
|
<div>valueMissing:<span x-text="valueMissing" class="font-bold" ></span></div>
|
|
90
91
|
<div>typeMismatch:<span x-text="typeMismatch" class="font-bold" ></span></div>
|
|
92
|
+
<div>serverErrorFields[form{{getRandom}}][name]:<span x-text="getServerErrorFields('{{_name}}')" class="font-bold" ></span></div>
|
|
93
|
+
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
94
|
+
|
|
91
95
|
</div>
|
|
92
96
|
</div>
|
|
93
97
|
</div>
|
|
@@ -5,38 +5,46 @@
|
|
|
5
5
|
<h3 class="mb-6 text-2xl font-headingSerif sm:mb-12">
|
|
6
6
|
{{this.title}}
|
|
7
7
|
</h3>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
<div id="formWrapper">
|
|
9
|
+
<form
|
|
10
|
+
x-ref="form{{nextRandom}}"
|
|
11
|
+
ax-load
|
|
12
|
+
x-data="contactForm('form{{getRandom}}','{{this.jsonURL}}','{{this.errorMessages}}','{{this.isMultipart}}','{{this.trackingInformations}}')"
|
|
13
|
+
x-init="formInit()"
|
|
14
|
+
x-ignore
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
@submit.prevent="submitData"
|
|
17
|
+
id="form{{getRandom}}"
|
|
18
|
+
class="relative flex flex-col justify-center overflow-hidden group"
|
|
19
|
+
action="{{this.url}}"
|
|
20
|
+
method="post"
|
|
21
|
+
enctype="{{if this.isMultipart 'multipart/form-data' 'application/x-www-form-urlencoded'}}"
|
|
22
|
+
accept-charset="utf-8"
|
|
23
|
+
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
<div class="">
|
|
27
|
+
<div class="px-4 py-2 font-bold text-white bg-red-500 rounded-t">
|
|
28
|
+
DEBUG
|
|
29
|
+
</div>
|
|
30
|
+
<div class="px-4 py-3 text-red-700 bg-red-100 border border-t-0 border-red-400 rounded-b">
|
|
31
|
+
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
32
|
+
<div>isPosting:<span x-text="isPosting" class="font-bold" :class="isPosting ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
33
|
+
|
|
34
|
+
</div>
|
|
29
35
|
</div>
|
|
30
|
-
|
|
31
|
-
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
{{> components/forms/fields _formId=(joinStrings 'form' (getRandom)) }}
|
|
36
|
+
{{> components/forms/fields _formId=(joinStrings 'form' (getRandom)) }}
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
{{> components/forms/controls }}
|
|
39
|
+
<template id="successMessage">
|
|
40
|
+
<h2>SUPER DAS HAT ALLES FUNKTIONIERT</h2>
|
|
41
|
+
</template>
|
|
42
|
+
<template id="errorMessage">
|
|
43
|
+
<h2>DAS HAT LEIDER NICHT FUNKTIONIERT</h2>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
</form>
|
|
47
|
+
</div>
|
|
40
48
|
{{/components/forms/backgroundBox }}
|
|
41
49
|
{{~else~}}
|
|
42
50
|
{{> content/webform/components/webform _addClass="print:hidden copytext__clearBox marginTrailer--m"}}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"repository": "https://github.com/szuelch/hr-design-system-handlebars",
|
|
9
|
-
"version": "1.108.
|
|
9
|
+
"version": "1.108.1",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
12
|
"storybook": "storybook dev -p 6006 public",
|
|
@@ -102,6 +102,7 @@
|
|
|
102
102
|
"remark-gfm": "^4.0.0",
|
|
103
103
|
"rimraf": "^3.0.2",
|
|
104
104
|
"storybook": "^8.2.6",
|
|
105
|
+
"storybook-addon-mock": "^5.0.0",
|
|
105
106
|
"storybook-conditional-toolbar-selector": "^1.0.3",
|
|
106
107
|
"style-loader": "^4.0.0",
|
|
107
108
|
"tailwindcss": "^3.0.23",
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
"isWebForm": true,
|
|
10
10
|
"hasNewWebForm": true,
|
|
11
11
|
"title": "Kontaktformular",
|
|
12
|
+
"jsonURL": "https://ugc-hessenschau.dev-ext.hrcms.gcp.cloud.hr.de",
|
|
13
|
+
"errorMessages": "errorMessages",
|
|
14
|
+
"isMultipart": "isMultipart",
|
|
15
|
+
"trackingInformations": "trackingInformations",
|
|
12
16
|
"fields":[
|
|
13
17
|
{
|
|
14
18
|
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
@@ -30,65 +34,8 @@
|
|
|
30
34
|
}
|
|
31
35
|
]
|
|
32
36
|
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"isHeadline": true,
|
|
37
|
-
"text": "Noch ein Formular"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"paragraphBoxItem": {
|
|
41
|
-
"isWebForm": true,
|
|
42
|
-
"hasNewWebForm": true,
|
|
43
|
-
"title": "Kontaktformular",
|
|
44
|
-
"fields":[
|
|
45
|
-
{
|
|
46
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
47
|
-
"@->contentpath": "input-text-vorname"
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
51
|
-
"@->contentpath": "input-text-vorname-required"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
55
|
-
"@->contentpath": "input-text-nachname-required"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
59
|
-
"@->contentpath": "select"
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
63
|
-
"@->contentpath": "input-email",
|
|
64
|
-
"@->overrides": [
|
|
65
|
-
{
|
|
66
|
-
"@->contentpath": "isRequired",
|
|
67
|
-
"@->value": false
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
73
|
-
"@->contentpath": "input-email"
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
77
|
-
"@->contentpath": "textarea"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"@->jsoninclude": "forms/form_fields.inc.json",
|
|
81
|
-
"@->contentpath": "checkbox",
|
|
82
|
-
"@->overrides": [
|
|
83
|
-
{
|
|
84
|
-
"@->contentpath": "label",
|
|
85
|
-
"@->value": "Ich bin damit einverstanden, dass der hr die von mir im vorstehenden Formular angegebenen personenbezogenen Daten für den Zweck der Kontaktaufnahme mit Upload verarbeitet. Eine Weitergabe an Dritte findet nicht statt, es sei denn, es wird ausdrücklich darauf hingewiesen. Unsere Datenschutzerklärung mit sämtlichen Informationen gemäß Art 13 DSGVO zur Datenverarbeitung durch den hr und zu Ihren Rechten können Sie unter Datenschutzerklärung einsehen. Den Datenschutzbeauftragten des hr erreichen Sie unter datenschutz@hr.de."
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
|
|
91
37
|
}
|
|
92
38
|
}
|
|
39
|
+
|
|
93
40
|
]
|
|
94
41
|
}
|
package/src/assets/js/alpine.js
CHANGED
|
@@ -53,7 +53,8 @@ Alpine.store('sharingBottomPos', {
|
|
|
53
53
|
current: '0'
|
|
54
54
|
})
|
|
55
55
|
Alpine.store('forms', {
|
|
56
|
-
submissionAttempted: []
|
|
56
|
+
submissionAttempted: [],
|
|
57
|
+
serverErrorFields: []
|
|
57
58
|
})
|
|
58
59
|
// Initialization of data handlers
|
|
59
60
|
Alpine.data('mainNavigationHandler', mainNavigationHandler)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import copytext from './copytext.hbs'
|
|
2
|
+
|
|
3
|
+
import copytext_webform_json from './fixtures/copytext_webform.json'
|
|
4
|
+
|
|
5
|
+
const Template = ({ ...args }) => {
|
|
6
|
+
return copytext({ ...args })
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: 'Komponenten/Content/Copytext',
|
|
11
|
+
decorators: [
|
|
12
|
+
(Story) => {
|
|
13
|
+
return `<div class="grid grid-page">
|
|
14
|
+
<div class="grid bg-white grid-article">
|
|
15
|
+
${Story()}
|
|
16
|
+
</div>
|
|
17
|
+
</div>`
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
parameters: {
|
|
21
|
+
mockData: [
|
|
22
|
+
{
|
|
23
|
+
url: 'https://ugc-hessenschau.dev-ext.hrcms.gcp.cloud.hr.de',
|
|
24
|
+
method: 'POST',
|
|
25
|
+
status: 200,
|
|
26
|
+
response: {
|
|
27
|
+
"status":"VALIDATION_ERROR","errors":{"vorname":"form_error_required"},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
layout: 'fullscreen',
|
|
32
|
+
chromatic: { disableSnapshot: true }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const WithWebform = {
|
|
37
|
+
render: Template.bind({}),
|
|
38
|
+
name: 'Formular',
|
|
39
|
+
args: copytext_webform_json,
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -17,7 +17,6 @@ import copytext_video_json from './fixtures/copytext_video.json'
|
|
|
17
17
|
import copytext_audio_json from './fixtures/copytext_audio.json'
|
|
18
18
|
import copytext_audio_event_stream_json from './fixtures/copytext_audio_livestream.json'
|
|
19
19
|
import copytext_livestream_json from './fixtures/copytext_livestream.json'
|
|
20
|
-
import copytext_webform_json from './fixtures/copytext_webform.json'
|
|
21
20
|
|
|
22
21
|
const Template = ({ ...args }) => {
|
|
23
22
|
return copytext({ ...args })
|
|
@@ -67,12 +66,6 @@ export const WithFiledownload = {
|
|
|
67
66
|
args: copytext_filedownload_json,
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
export const WithWebform = {
|
|
71
|
-
render: Template.bind({}),
|
|
72
|
-
name: 'Formular',
|
|
73
|
-
args: copytext_webform_json,
|
|
74
|
-
}
|
|
75
|
-
|
|
76
69
|
export const WithImage = {
|
|
77
70
|
render: Template.bind({}),
|
|
78
71
|
name: 'Image',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"copytextParagraph":[{"isHeadline":true,"text":"Copytext mit Formular"},{"paragraphBoxItem":{"isWebForm":true,"hasNewWebForm":true,"title":"Kontaktformular","
|
|
1
|
+
{"copytextParagraph":[{"isHeadline":true,"text":"Copytext mit Formular"},{"paragraphBoxItem":{"isWebForm":true,"hasNewWebForm":true,"title":"Kontaktformular","jsonURL":"https://ugc-hessenschau.dev-ext.hrcms.gcp.cloud.hr.de","errorMessages":"errorMessages","isMultipart":"isMultipart","trackingInformations":"trackingInformations","fields":[{"type":{"isText":true,"asString":"text"},"name":"vorname","label":"Vorname","description":"Das ist der Beschreibungstext (*Pflichtfeld)","defaultValue":"","isHidden":false,"isRequired":true,"maxLength":"140"},{"type":{"isText":true,"asString":"text"},"name":"nachname","label":"Nachname","description":"","defaultValue":"","isHidden":false,"isRequired":true,"maxLength":"140"},{"isGrouped":false,"type":{"isChoice":true,"asString":"checkbox"},"name":"checkbox","label":"Ich bin damit einverstanden, dass der hr die von mir im vorstehenden Formular angegebenen personenbezogenen Daten für den Zweck der Kontaktaufnahme mit Upload verarbeitet. Eine Weitergabe an Dritte findet nicht statt, es sei denn, es wird ausdrücklich darauf hingewiesen. Unsere Datenschutzerklärung mit sämtlichen Informationen gemäß Art 13 DSGVO zur Datenverarbeitung durch den hr und zu Ihren Rechten können Sie unter Datenschutzerklärung einsehen. Den Datenschutzbeauftragten des hr erreichen Sie unter datenschutz@hr.de.","isMeta":false,"description":"Das ist der Beschreibungstext von Checkbox","isRequired":true}]}}]}
|
|
@@ -1,36 +1,199 @@
|
|
|
1
|
-
export default function contactForm(formId) {
|
|
1
|
+
export default function contactForm(formId, jsonURL, errorMessages, multipart, trackingInformations, jsonp = false) {
|
|
2
2
|
return {
|
|
3
|
+
isPosting: false,
|
|
4
|
+
isWebview:false,
|
|
5
|
+
ajaxTimeout: 60 * 1000,
|
|
6
|
+
form: this.$refs[formId],
|
|
7
|
+
formWrapper: this.$refs[formId].closest("#formWrapper"),
|
|
8
|
+
actionUrl: this.form && this.form.getAttribute('action'),
|
|
9
|
+
checkForJsonURL () {
|
|
10
|
+
if (jsonURL) {
|
|
11
|
+
this.actionUrl = jsonURL
|
|
12
|
+
}
|
|
13
|
+
},
|
|
3
14
|
formInit(){
|
|
4
|
-
this
|
|
5
|
-
console.log("
|
|
15
|
+
this.checkForJsonURL()
|
|
16
|
+
console.log("%cformId:", 'color: green' ,formId);
|
|
17
|
+
console.log("%cform:", 'color: green' ,this.form);
|
|
18
|
+
console.log("%cformWrapper:", 'color: green' ,this.formWrapper);
|
|
19
|
+
console.log("%cactionUrl:", 'color: green' ,this.actionUrl);
|
|
20
|
+
console.log("%cjsonURL:", 'color: green', jsonURL);
|
|
21
|
+
console.log("%cerrorMessages:", 'color: green', errorMessages);
|
|
22
|
+
console.log("%cmultipart:", 'color: green', multipart);
|
|
23
|
+
console.log("%ctrackingInformations:", 'color: green', trackingInformations);
|
|
24
|
+
|
|
25
|
+
this.$store.forms.submissionAttempted[formId] = false;
|
|
6
26
|
},
|
|
7
|
-
clickHandler() {
|
|
8
|
-
console.log(
|
|
9
|
-
|
|
10
|
-
console.log('form',form);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if(form.reportValidity()){
|
|
17
|
-
this.submitData();
|
|
27
|
+
clickHandler(event) {
|
|
28
|
+
console.log("event:",event);
|
|
29
|
+
console.log('check for Error:',formId);
|
|
30
|
+
console.log('form:',this.form);
|
|
31
|
+
|
|
32
|
+
if(this.form.reportValidity()){
|
|
33
|
+
// this.logData(event,form);
|
|
34
|
+
this.handleSubmit(event,this.form)
|
|
18
35
|
} else {
|
|
19
36
|
this.$store.forms.submissionAttempted[formId] = true;
|
|
20
37
|
}
|
|
21
38
|
},
|
|
22
|
-
|
|
23
|
-
|
|
39
|
+
logData(event,form) {
|
|
40
|
+
// TODO - FOR DEBUGGIN CN BE REMOVED AT THE END
|
|
41
|
+
const formData = new FormData(form);
|
|
42
|
+
const fields = Array.from(form.elements);
|
|
43
|
+
// Log the serialized form data
|
|
44
|
+
console.log(fields);
|
|
45
|
+
|
|
24
46
|
// Convert the FormData to a serialized string
|
|
25
47
|
const serializedData = Array.from(formData.entries())
|
|
26
48
|
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
27
49
|
.join('&');
|
|
28
50
|
|
|
29
51
|
// Log the serialized form data
|
|
30
|
-
console.log('DATA: ' + serializedData);
|
|
52
|
+
console.log('serialized DATA: ' + serializedData);
|
|
53
|
+
console.log('DATA:', new URLSearchParams(new FormData(form)).toString());
|
|
54
|
+
|
|
55
|
+
},
|
|
56
|
+
// TODO - Validation error handler (adapt to your case)
|
|
57
|
+
handleValidationErrors(errors) {
|
|
58
|
+
console.log('Validation Errors:', errors);
|
|
59
|
+
this.$store.forms.serverErrorFields[formId] = errors;
|
|
60
|
+
console.log('Validation Errors in Store:', this.$store.forms.serverErrorFields);
|
|
61
|
+
},
|
|
62
|
+
resetValidationErrors() {
|
|
63
|
+
this.$store.forms.serverErrorFields[formId] = {}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
handleSubmit(event, form ) {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
|
|
69
|
+
if (!this.isWebview) {
|
|
70
|
+
//uxAction(trackingInformations); // Assuming this is a tracking library
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.isPosting) return;
|
|
74
|
+
this.isPosting = true;
|
|
75
|
+
|
|
76
|
+
// TODO mit alpine umsetzen
|
|
77
|
+
//const preloadIcon = formWrapper.querySelector('.js-preloadIcon');
|
|
78
|
+
//const loadingIcon = formWrapper.querySelector('.js-loadingIcon');
|
|
79
|
+
|
|
80
|
+
// Show loading indicator
|
|
81
|
+
//preloadIcon.classList.add('-isHidden');
|
|
82
|
+
//loadingIcon.classList.remove('-isHidden');
|
|
83
|
+
|
|
84
|
+
console.log('DATA:', new URLSearchParams(new FormData(form)).toString());
|
|
85
|
+
|
|
86
|
+
// Define ajaxOptions based on form type (without jQuery $.ajax)
|
|
87
|
+
let ajaxOptions = {
|
|
88
|
+
method: jsonp ? 'GET' : 'POST',
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
|
91
|
+
},
|
|
92
|
+
timeout: this.ajaxTimeout,
|
|
93
|
+
body: new URLSearchParams(new FormData(form)),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (jsonp) {
|
|
97
|
+
ajaxOptions.url = this.actionUrl;
|
|
98
|
+
ajaxOptions.headers['Content-Type'] = 'application/json; charset=utf-8';
|
|
99
|
+
ajaxOptions.body = new URLSearchParams(new FormData(form)).toString(); // For JSONP case
|
|
100
|
+
} else if (multipart) {
|
|
101
|
+
ajaxOptions = {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
url: this.actionUrl,
|
|
104
|
+
timeout: 600 * 1000,
|
|
105
|
+
body: new FormData(form), // No URLSearchParams, we use FormData for multipart
|
|
106
|
+
processData: false,
|
|
107
|
+
headers: {}
|
|
108
|
+
};
|
|
109
|
+
} else {
|
|
110
|
+
ajaxOptions.url = `${this.actionUrl}?${responseFormatParam}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Using fetch API instead of $.ajax
|
|
114
|
+
fetch(ajaxOptions.url, ajaxOptions)
|
|
115
|
+
.then(async (response) => {
|
|
116
|
+
const data = await response.text(); // Assuming the rponse is text or JSON
|
|
117
|
+
if (response.ok) {
|
|
118
|
+
console.log('Done');
|
|
119
|
+
console.log(data);
|
|
120
|
+
if (jsonURL) {
|
|
121
|
+
const responseData = JSON.parse(data);
|
|
122
|
+
switch (responseData.status) {
|
|
123
|
+
case 'VALIDATION_ERROR':
|
|
124
|
+
this.resetValidationErrors();
|
|
125
|
+
this.handleValidationErrors(responseData.errors);
|
|
126
|
+
break;
|
|
127
|
+
case 'OK':
|
|
128
|
+
console.log("OK");
|
|
129
|
+
this.replaceAnimated(
|
|
130
|
+
this.formWrapper,
|
|
131
|
+
this.form.querySelector('#successMessage').innerHTML,
|
|
132
|
+
true
|
|
133
|
+
);
|
|
134
|
+
break;
|
|
135
|
+
default:
|
|
136
|
+
console.log("default");
|
|
137
|
+
replaceAnimated(
|
|
138
|
+
this.formWrapper,
|
|
139
|
+
this.form.querySelector('#errorMessage').innerHTML,
|
|
140
|
+
true
|
|
141
|
+
);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
replaceAnimated(this.formWrapper, data, true);
|
|
146
|
+
}
|
|
147
|
+
/*
|
|
148
|
+
if (eventOnSuccess) {
|
|
149
|
+
fireEvent(eventOnSuccess, true);
|
|
150
|
+
} */
|
|
151
|
+
|
|
152
|
+
/* if (rootElement.id) {
|
|
153
|
+
window.location.hash = rootElement.id;
|
|
154
|
+
} */
|
|
155
|
+
} else {
|
|
156
|
+
throw new Error('Network response was not ok.');
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
.catch((error) => {
|
|
160
|
+
console.error('Fail:', error);
|
|
161
|
+
replaceAnimated(
|
|
162
|
+
this.formWrapper,
|
|
163
|
+
'<div class="c-form success">Das hat leider nicht funktioniert!</div>',
|
|
164
|
+
true
|
|
165
|
+
);
|
|
166
|
+
})
|
|
167
|
+
.finally(() => {
|
|
168
|
+
console.log('Always');
|
|
169
|
+
// TODO MIT ALPINE UMSETZEN
|
|
170
|
+
//preloadIcon.classList.remove('-isHidden');
|
|
171
|
+
//loadingIcon.classList.add('-isHidden');
|
|
172
|
+
this.isPosting = false;
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
// Helper function to replace content with animation (replacing hrQuery's replaceAnimated)
|
|
176
|
+
replaceAnimated(wrapper, newContent, withFade = true) {
|
|
177
|
+
if (withFade) {
|
|
178
|
+
wrapper.style.opacity = 0;
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
wrapper.innerHTML = newContent;
|
|
181
|
+
wrapper.style.opacity = 1;
|
|
182
|
+
}, 300);
|
|
183
|
+
} else {
|
|
184
|
+
wrapper.innerHTML = newContent;
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
// Fire event utility (could be Alpine.js specific or custom)
|
|
188
|
+
fireEvent(eventName, success){
|
|
189
|
+
const event = new CustomEvent(eventName, { detail: success });
|
|
190
|
+
document.dispatchEvent(event);
|
|
31
191
|
},
|
|
32
192
|
getSubmissionAttempted() {
|
|
33
193
|
return this.$store.forms.submissionAttempted[formId]
|
|
34
194
|
}
|
|
195
|
+
|
|
35
196
|
}
|
|
36
|
-
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
<div class="text-xs text-gray-500 font-headingSerif">Pflichtfeld*</div>
|
|
3
3
|
<div class="flex items-center">
|
|
4
4
|
<label class="order-2 cursor-pointer {{> components/button/utilities/button_base_classes}} {{> components/button/utilities/button_variation_classes _variant='primary'}} {{> components/button/utilities/button_dimension_classes _size='lg'}}">
|
|
5
|
-
{{> components/base/image/icon _icon="
|
|
6
|
-
<
|
|
5
|
+
<span class="hidden" :class="{'hidden': !isPosting}">{{> components/base/image/icon _icon="reload" _addClass="w-5 h-5 fill-white dark:fill-text-dark animate-spin"}}</span>
|
|
6
|
+
<span class="" :class="{'hidden': isPosting}">{{> components/base/image/icon _icon="send-ds" _addClass="w-5 h-5 fill-white dark:fill-text-dark "}}</span>
|
|
7
|
+
<input type="submit" class="pl-2 cursor-pointer" value="Absenden" @click.prevent="clickHandler($event)"/>
|
|
7
8
|
</label>
|
|
8
9
|
|
|
9
10
|
{{#> components/button/button _variant="tertiary"_size="lg" _css="order-1 mr-4" _type="reset"}}
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
DEBUG
|
|
78
78
|
</div>
|
|
79
79
|
<div class="px-4 py-3 text-red-700 bg-red-100 border border-t-0 border-red-400 rounded-b">
|
|
80
|
+
<div>name:<span x-text="name" class="font-bold" ></span></div>
|
|
80
81
|
<div>isFocused:<span x-text="isFocused" class="font-bold" :class="isFocused ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
81
82
|
<div>wasFocused:<span x-text="wasFocused" class="font-bold" :class="wasFocused ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
82
83
|
<div>valid:<span x-text="valid" class="font-bold" :class="valid ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
@@ -88,6 +89,9 @@
|
|
|
88
89
|
<div>errorMessage:<span x-text="errorMessage" class="font-bold" ></span></div>
|
|
89
90
|
<div>valueMissing:<span x-text="valueMissing" class="font-bold" ></span></div>
|
|
90
91
|
<div>typeMismatch:<span x-text="typeMismatch" class="font-bold" ></span></div>
|
|
92
|
+
<div>serverErrorFields[form{{getRandom}}][name]:<span x-text="getServerErrorFields('{{_name}}')" class="font-bold" ></span></div>
|
|
93
|
+
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
94
|
+
|
|
91
95
|
</div>
|
|
92
96
|
</div>
|
|
93
97
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default function inputHandler(element, formId, errorMandatory, type, errorEmail, prefilledText = '') {
|
|
2
2
|
return {
|
|
3
3
|
[element]: prefilledText,
|
|
4
|
+
name: document.getElementById(element).getAttribute("name"),
|
|
4
5
|
valid: false,
|
|
5
6
|
wasFocused: false,
|
|
6
7
|
isFocused: false,
|
|
@@ -13,19 +14,23 @@ export default function inputHandler(element, formId, errorMandatory, type, erro
|
|
|
13
14
|
return this.valueMissing ? errorMandatory : errorEmail
|
|
14
15
|
}
|
|
15
16
|
else {
|
|
16
|
-
|
|
17
|
+
if(this.hasServerError()){
|
|
18
|
+
return this.getServerError()
|
|
19
|
+
} else {
|
|
20
|
+
return errorMandatory
|
|
21
|
+
}
|
|
17
22
|
}
|
|
18
23
|
},
|
|
19
24
|
hideDescription() {
|
|
20
25
|
switch (type) {
|
|
21
26
|
case "email":
|
|
22
|
-
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (this.typeMismatch && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]));
|
|
27
|
+
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (this.typeMismatch && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ));
|
|
23
28
|
case "checkbox":
|
|
24
|
-
return Boolean(!this.valid && this.wasFocused && !this.isFocused && !this.isChecked || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]));
|
|
29
|
+
return Boolean(!this.valid && this.wasFocused && !this.isFocused && !this.isChecked || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ));
|
|
25
30
|
case "select":
|
|
26
|
-
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]))
|
|
31
|
+
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ))
|
|
27
32
|
default:
|
|
28
|
-
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]));
|
|
33
|
+
return Boolean((!this.valid && this.wasFocused && !this.isFocused) || (!this.valid && !this.isFocused && this.$store.forms.submissionAttempted[formId]) || (this.hasServerError() && !this.isFocused ));
|
|
29
34
|
}
|
|
30
35
|
},
|
|
31
36
|
hideError() {
|
|
@@ -36,6 +41,48 @@ export default function inputHandler(element, formId, errorMandatory, type, erro
|
|
|
36
41
|
this.typeMismatch = field.validity.typeMismatch;
|
|
37
42
|
this.valueMissing = field.validity.valueMissing;
|
|
38
43
|
this.valid = field.checkValidity()
|
|
44
|
+
},
|
|
45
|
+
hasServerError() {
|
|
46
|
+
return Boolean(this.$store.forms.serverErrorFields[formId][this.name])
|
|
47
|
+
},
|
|
48
|
+
getServerError() {
|
|
49
|
+
let serverError = "Server Error: "
|
|
50
|
+
switch (this.$store.forms.serverErrorFields[formId][this.name]) {
|
|
51
|
+
|
|
52
|
+
case 'form_error_required':
|
|
53
|
+
serverError += "Pflichtfeld"
|
|
54
|
+
break
|
|
55
|
+
case 'form_error_max':
|
|
56
|
+
serverError += "Zu viele Zeichen"
|
|
57
|
+
break
|
|
58
|
+
case 'form_error_validurl':
|
|
59
|
+
serverError += "Ungültige URL"
|
|
60
|
+
break
|
|
61
|
+
case 'form_error_empty':
|
|
62
|
+
serverError += "Darf nicht ausgefüllt werden"
|
|
63
|
+
break
|
|
64
|
+
case 'form_error_constants_or_null':
|
|
65
|
+
serverError += "Ungültiger Wert"
|
|
66
|
+
break
|
|
67
|
+
case 'form_error_constants':
|
|
68
|
+
serverError += "Ungültiger Wert"
|
|
69
|
+
break
|
|
70
|
+
case 'form_error_max_multivalue':
|
|
71
|
+
serverError += "Die maximale Anzahl an Antwortmöglichkeiten wurde überschritten"
|
|
72
|
+
break
|
|
73
|
+
case 'vote_error_identity_already_used':
|
|
74
|
+
serverError += "Unter dieser E-Mail-Adresse wurde bereits abgestimmt. Eine weitere Abstimmung ist nicht möglich."
|
|
75
|
+
break
|
|
76
|
+
case 'vote_error_token_request_count_exceeded':
|
|
77
|
+
serverError += "Die maximale Anzahl an Bestätigungs-E-Mails wurde bereits verschickt."
|
|
78
|
+
break
|
|
79
|
+
case 'form_error_email':
|
|
80
|
+
serverError += "Ungültige E-Mail-Adresse"
|
|
81
|
+
break
|
|
82
|
+
default:
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
return serverError
|
|
39
86
|
}
|
|
40
87
|
};
|
|
41
88
|
}
|
|
@@ -5,38 +5,46 @@
|
|
|
5
5
|
<h3 class="mb-6 text-2xl font-headingSerif sm:mb-12">
|
|
6
6
|
{{this.title}}
|
|
7
7
|
</h3>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
<div id="formWrapper">
|
|
9
|
+
<form
|
|
10
|
+
x-ref="form{{nextRandom}}"
|
|
11
|
+
ax-load
|
|
12
|
+
x-data="contactForm('form{{getRandom}}','{{this.jsonURL}}','{{this.errorMessages}}','{{this.isMultipart}}','{{this.trackingInformations}}')"
|
|
13
|
+
x-init="formInit()"
|
|
14
|
+
x-ignore
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
@submit.prevent="submitData"
|
|
17
|
+
id="form{{getRandom}}"
|
|
18
|
+
class="relative flex flex-col justify-center overflow-hidden group"
|
|
19
|
+
action="{{this.url}}"
|
|
20
|
+
method="post"
|
|
21
|
+
enctype="{{if this.isMultipart 'multipart/form-data' 'application/x-www-form-urlencoded'}}"
|
|
22
|
+
accept-charset="utf-8"
|
|
23
|
+
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
<div class="">
|
|
27
|
+
<div class="px-4 py-2 font-bold text-white bg-red-500 rounded-t">
|
|
28
|
+
DEBUG
|
|
29
|
+
</div>
|
|
30
|
+
<div class="px-4 py-3 text-red-700 bg-red-100 border border-t-0 border-red-400 rounded-b">
|
|
31
|
+
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
32
|
+
<div>isPosting:<span x-text="isPosting" class="font-bold" :class="isPosting ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
33
|
+
|
|
34
|
+
</div>
|
|
29
35
|
</div>
|
|
30
|
-
|
|
31
|
-
<div>submissionAttempted[form{{getRandom}}]:<span x-text="getSubmissionAttempted()" class="font-bold" :class="getSubmissionAttempted() ? 'text-green-800' : 'text-red-700'"></span></div>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
{{> components/forms/fields _formId=(joinStrings 'form' (getRandom)) }}
|
|
36
|
+
{{> components/forms/fields _formId=(joinStrings 'form' (getRandom)) }}
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
{{> components/forms/controls }}
|
|
39
|
+
<template id="successMessage">
|
|
40
|
+
<h2>SUPER DAS HAT ALLES FUNKTIONIERT</h2>
|
|
41
|
+
</template>
|
|
42
|
+
<template id="errorMessage">
|
|
43
|
+
<h2>DAS HAT LEIDER NICHT FUNKTIONIERT</h2>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
</form>
|
|
47
|
+
</div>
|
|
40
48
|
{{/components/forms/backgroundBox }}
|
|
41
49
|
{{~else~}}
|
|
42
50
|
{{> content/webform/components/webform _addClass="print:hidden copytext__clearBox marginTrailer--m"}}
|