@gudhub/ssg-web-components-library 1.0.103 → 1.0.104

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gudhub/ssg-web-components-library",
3
- "version": "1.0.103",
3
+ "version": "1.0.104",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -26,6 +26,8 @@ class BreadcrumbsComponent extends GHComponent {
26
26
 
27
27
  this.items === null ? console.error(`Didn't find current route in config, current URL: ${currentUrl}`) : null;
28
28
 
29
+ console.log("this.items:", this.items);
30
+
29
31
  if (this.items) {
30
32
  super.render(html);
31
33
  }
@@ -26,6 +26,7 @@ class GetInTouchBlock extends GHComponent {
26
26
  this.subtitleName = this.hasAttribute('data-form-subtitle') ? this.getAttribute('data-form-subtitle') : null;
27
27
  this.placement = this.hasAttribute('data-form-placement') ? this.getAttribute('data-form-placement') : "main";
28
28
  this.buttonText = this.hasAttribute('data-form-button-text') ? this.getAttribute('data-form-button-text') : null;
29
+ this.recaptchaSiteKey = this.hasAttribute('data-recaptcha-site-key') ? this.getAttribute('data-recaptcha-site-key') : null;
29
30
 
30
31
  const attributes = [{
31
32
  'data-form-id': this.formId,
@@ -33,6 +34,7 @@ class GetInTouchBlock extends GHComponent {
33
34
  'data-form-subtitle': this.subtitleName,
34
35
  'data-form-placement': this.placement,
35
36
  'data-form-button-text': this.buttonText,
37
+ 'data-recaptcha-site-key': this.recaptchaSiteKey,
36
38
  }];
37
39
 
38
40
  const attributesString = attributes.reduce((acc, obj) => {
@@ -47,4 +49,5 @@ class GetInTouchBlock extends GHComponent {
47
49
  return attributesString;
48
50
  }
49
51
  }
50
- window.customElements.define('get-in-touch-block', GetInTouchBlock);
52
+
53
+ window.customElements.define('get-in-touch-block', GetInTouchBlock);
@@ -1,12 +1,39 @@
1
1
  import html from './get-in-touch-form.html';
2
2
  import './get-in-touch-form.scss';
3
+
3
4
  import defaultConfigs from './get-in-touch-form-data.json';
4
5
  import { checkInputsValidations } from './inputsValidation.js';
5
6
 
6
- class GetInTouchForm extends GHComponent {
7
+ let recaptchaPromise = null;
8
+
9
+ function loadRecaptcha(siteKey) {
10
+ if (window.grecaptcha) return Promise.resolve(window.grecaptcha);
11
+
12
+ if (recaptchaPromise) return recaptchaPromise;
13
+
14
+ recaptchaPromise = new Promise((resolve, reject) => {
15
+ const script = document.createElement('script');
16
+ script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
17
+ script.async = true;
18
+ script.defer = true;
19
+
20
+ script.onload = () => {
21
+ if (window.grecaptcha) resolve(window.grecaptcha);
22
+ else reject('grecaptcha not available');
23
+ };
24
+
25
+ script.onerror = reject;
26
+
27
+ document.head.appendChild(script);
28
+ });
29
+
30
+ return recaptchaPromise;
31
+ }
7
32
 
33
+ class GetInTouchForm extends GHComponent {
8
34
  constructor() {
9
35
  super();
36
+
10
37
  this.formId = this.getAttribute("data-form-id");
11
38
  this.defaultConfigs = defaultConfigs;
12
39
 
@@ -15,12 +42,14 @@ class GetInTouchForm extends GHComponent {
15
42
 
16
43
  this.placement = 'main';
17
44
  this.config = window.getConfig()?.componentsConfigs?.formConfig || window.getConfig()?.formConfig;
45
+
46
+ this.recaptcha_site_key = this.hasAttribute('data-recaptcha-site-key')
47
+ ? this.getAttribute('data-recaptcha-site-key')
48
+ : null;
18
49
  }
19
50
 
20
51
  onServerRender() {
21
- if (this.hasAttribute('data-in-popup')) {
22
- return;
23
- }
52
+ if (this.hasAttribute('data-in-popup')) return;
24
53
 
25
54
  this.initConfig(this.config);
26
55
  super.render(html);
@@ -30,6 +59,8 @@ class GetInTouchForm extends GHComponent {
30
59
  if (!this.hasAttribute('data-in-popup')) {
31
60
  this.initConfig(this.config);
32
61
  this.attachEventListeners();
62
+
63
+ loadRecaptcha(this.recaptcha_site_key).catch(console.error);
33
64
  }
34
65
  }
35
66
 
@@ -40,8 +71,11 @@ class GetInTouchForm extends GHComponent {
40
71
  }
41
72
 
42
73
  attachEventListeners() {
43
- this.getElementsByTagName('form')[0].addEventListener('submit', (e) => this.handleSubmit(e));
44
- this.getElementsByClassName('restart_button')[0].addEventListener('click', (e) => this.hideFail());
74
+ this.getElementsByTagName('form')[0]
75
+ .addEventListener('submit', (e) => this.handleSubmit(e));
76
+
77
+ this.getElementsByClassName('restart_button')[0]
78
+ .addEventListener('click', () => this.hideFail());
45
79
  }
46
80
 
47
81
  onParentPopupClose() {
@@ -56,53 +90,70 @@ class GetInTouchForm extends GHComponent {
56
90
  initConfig(formConfigs) {
57
91
  try {
58
92
  this.config = formConfigs.find(({ id }) => id === this.formId);
59
- if (!this.config) {
60
- throw new Error("Config not found");
61
- }
62
- } catch (error) {
93
+ if (!this.config) throw new Error("Config not found");
94
+ } catch {
63
95
  const defaultId = this.isInPopup ? 'default popup' : 'default';
64
96
  this.config = defaultConfigs.find(({ id }) => id === defaultId);
65
97
  }
66
98
 
67
- this.titleName = this.hasAttribute('data-form-title') ? this.getAttribute('data-form-title') : this.config.title;
68
- this.subtitleName = this.hasAttribute('data-form-subtitle') ? this.getAttribute('data-form-subtitle') : this.config.subtitle;
69
- this.placement = this.hasAttribute('data-form-placement') ? this.getAttribute('data-form-placement') : "main";
70
- this.buttonText = this.hasAttribute('data-form-button-text') ? this.getAttribute('data-form-button-text') : this.config.button_text;
71
- };
99
+ this.titleName = this.getAttribute('data-form-title') || this.config.title;
100
+ this.subtitleName = this.getAttribute('data-form-subtitle') || this.config.subtitle;
101
+ this.placement = this.getAttribute('data-form-placement') || "main";
102
+ this.buttonText = this.getAttribute('data-form-button-text') || this.config.button_text;
103
+ }
104
+
105
+ async getRecaptchaToken(action = 'submit') {
106
+ const grecaptcha = await loadRecaptcha(this.recaptcha_site_key);
107
+
108
+ return new Promise((resolve, reject) => {
109
+ grecaptcha.ready(() => {
110
+ grecaptcha.execute(this.recaptcha_site_key, { action })
111
+ .then(resolve)
112
+ .catch(reject);
113
+ });
114
+ });
115
+ }
72
116
 
73
117
  async handleSubmit(event) {
74
118
  event.preventDefault();
75
119
  const form = event.target;
76
-
77
- const validationResults = await this.inputsValidation(form); //await because we can make async check (for example: email verify for send opportunity)
120
+
121
+ const validationResults = await this.inputsValidation(form);
78
122
 
79
123
  if (validationResults.every(({ isValid }) => isValid)) {
80
124
  this.addLoader();
125
+
81
126
  try {
82
- await this.fetchExample();
127
+ const token = await this.getRecaptchaToken();
128
+
129
+ await this.fetchExample(token);
130
+
131
+ this.handleSuccessFormValidation(form, token);
83
132
 
84
- this.handleSuccessFormValidation(form);
85
133
  } catch (error) {
86
134
  console.error(error);
87
135
  this.showFail();
88
136
  }
89
137
 
90
- this.removeLoader(form);
138
+ this.removeLoader();
91
139
 
92
140
  } else {
93
- //show error on correspond input
94
- const validationResultsForErrors = validationResults.filter(item => typeof item === 'object')
95
- validationResultsForErrors.forEach(({ input, isValid }) => this.toggleError(input, isValid));
141
+ validationResults
142
+ .filter(item => typeof item === 'object')
143
+ .forEach(({ input, isValid }) => this.toggleError(input, isValid));
96
144
  }
97
145
  }
98
146
 
99
- handleSuccessFormValidation = (form) => {
147
+ handleSuccessFormValidation = (form, token) => {
100
148
  const emailInput = form.querySelector('[name="email"]');
101
149
  const phoneInput = form.querySelector('[name="phone"]');
102
150
 
103
- this.showSuccess({ email: emailInput ? emailInput.value : '', phone: phoneInput ? phoneInput.value : '' });
151
+ this.showSuccess({
152
+ email: emailInput?.value || '',
153
+ phone: phoneInput?.value || ''
154
+ });
104
155
 
105
- const formDataObj = this.createDataObject(form, this.placement)
156
+ const formDataObj = this.createDataObject(form, this.placement, token);
106
157
 
107
158
  window.dispatchEvent(new CustomEvent('submitForm', { detail: { formDataObj } }));
108
159
 
@@ -112,38 +163,31 @@ class GetInTouchForm extends GHComponent {
112
163
 
113
164
  inputsValidation = async (form) => {
114
165
  const inputs = Array.from(form.querySelectorAll('input'));
115
-
116
- const validationResults = checkInputsValidations(inputs);
117
-
118
- return validationResults;
166
+ return checkInputsValidations(inputs);
119
167
  }
120
168
 
121
- fetchExample = () => {
169
+ fetchExample = (recaptchaToken) => {
122
170
  const isSuccess = true;
123
171
 
124
172
  return new Promise((resolve, reject) => {
125
- const TIMEOUT_DURATION = 2000;
173
+ console.log('reCAPTCHA token:', recaptchaToken);
126
174
 
127
175
  setTimeout(() => {
128
- if (isSuccess) {
129
- resolve();
130
- } else {
131
- reject();
132
- }
133
- }, TIMEOUT_DURATION);
176
+ isSuccess ? resolve() : reject();
177
+ }, 2000);
134
178
  });
135
179
  }
136
-
137
- createDataObject(form, placement) {
180
+
181
+ createDataObject(form, placement, recaptchaToken) {
138
182
  const inputs = {};
139
183
 
140
- for (const [name, value] of (new FormData(form)).entries()) {
184
+ for (const [name, value] of new FormData(form).entries()) {
141
185
  inputs[name] = value;
142
186
  }
143
187
 
144
188
  const { id, mailConfig } = this.config;
145
189
 
146
- const formDataObj = {
190
+ return {
147
191
  inputs,
148
192
  mailConfig,
149
193
  website: window.location.hostname,
@@ -151,9 +195,8 @@ class GetInTouchForm extends GHComponent {
151
195
  formId: id,
152
196
  formPlacement: placement,
153
197
  referrer: localStorage.getItem('referrer'),
198
+ recaptchaToken
154
199
  };
155
-
156
- return formDataObj;
157
200
  }
158
201
 
159
202
  toggleError(input, isValid) {
@@ -161,49 +204,49 @@ class GetInTouchForm extends GHComponent {
161
204
  input.parentElement.classList[isValid ? 'remove' : 'add']('error-input');
162
205
  }
163
206
 
164
- async addLoader() {
207
+ addLoader() {
165
208
  this.classList.add('loading');
166
209
  this.querySelector('button[type="submit"]').disabled = true;
167
210
  }
168
211
 
169
- async removeLoader() {
212
+ removeLoader() {
170
213
  this.classList.remove('loading');
171
- const submitButton = this.querySelector('button[type="submit"]');
172
- setTimeout(() => {
173
- submitButton.disabled = false;
174
- }, 500);
214
+ const btn = this.querySelector('button[type="submit"]');
215
+ setTimeout(() => btn.disabled = false, 500);
175
216
  }
176
217
 
177
- async showSuccess({email, phone}) {
218
+ showSuccess({email, phone}) {
178
219
  if (email) {
179
220
  this.querySelector('.check_entity').classList.add('provided');
180
221
  this.getElementsByClassName('email')[0].innerText = email;
181
222
  }
182
- this.classList.add('success');
223
+
183
224
  if (phone) {
184
- this.querySelector('.check_entity.phone_entity').classList.add('provided');
225
+ this.querySelector('.phone_entity').classList.add('provided');
185
226
  this.getElementsByClassName('phone')[0].innerText = phone;
186
227
  }
228
+
187
229
  this.classList.add('success');
188
230
  }
189
231
 
190
- async showFail() {
232
+ showFail() {
191
233
  this.classList.add('fail');
192
234
  }
193
235
 
194
- async hideSuccess() {
236
+ hideSuccess() {
195
237
  this.getElementsByClassName('email')[0].innerText = '';
196
238
  this.getElementsByClassName('phone')[0].innerText = '';
197
- this.querySelector('.check_entity.phone_entity').classList.remove('provided');
239
+ this.querySelector('.phone_entity').classList.remove('provided');
198
240
  this.classList.remove('success');
199
241
  }
200
242
 
201
- async hideFail() {
202
- const overflowFail = this.querySelector('.overflow.fail');
203
- overflowFail.style.opacity = 0;
243
+ hideFail() {
244
+ const el = this.querySelector('.overflow.fail');
245
+ el.style.opacity = 0;
246
+
204
247
  setTimeout(() => {
205
248
  this.classList.remove('fail');
206
- overflowFail.style.opacity = '';
249
+ el.style.opacity = '';
207
250
  }, 500);
208
251
  }
209
252
 
@@ -218,10 +261,15 @@ class GetInTouchForm extends GHComponent {
218
261
 
219
262
  const tag = input.type === 'textarea' ? 'textarea' : 'input';
220
263
  const maxLength = tag === 'textarea' ? maxSymbols.long : maxSymbols[input.type];
264
+
221
265
  return acc + `
222
266
  <div class="input-wrap col-${input.width}">
223
267
  ${input.title ? `<label for="${input.name}">${input.title}</label>` : ''}
224
- <${tag} type="text" name=${input.name} ${input.placeholder ? `placeholder="${input.placeholder}"` : ''} ${JSON.parse(input.required) ? 'required' : ''} ${maxLength ? `maxlength=${maxLength}` : ''}></${tag}>
268
+ <${tag} name="${input.name}"
269
+ ${input.placeholder ? `placeholder="${input.placeholder}"` : ''}
270
+ ${JSON.parse(input.required) ? 'required' : ''}
271
+ ${maxLength ? `maxlength="${maxLength}"` : ''}>
272
+ </${tag}>
225
273
  ${input.type === 'email' || input.type === 'phone' ? `<span class="${input.type}-error">${input.errorText}</span>` : ''}
226
274
  </div>
227
275
  `;
@@ -231,4 +279,4 @@ class GetInTouchForm extends GHComponent {
231
279
 
232
280
  if (!customElements.get('get-in-touch-form')) {
233
281
  customElements.define('get-in-touch-form', GetInTouchForm);
234
- }
282
+ }