wickes-css2 2.117.0-develop.3 → 2.117.0-gift-cards.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.
Files changed (102) hide show
  1. package/Readme.md +0 -1
  2. package/build/css/category-main.css +1 -1
  3. package/build/css/components/checkout-payment-details-v2.css +1 -1
  4. package/build/css/homepage-main.css +1 -1
  5. package/build/css/kitchen-plp-main.css +1 -1
  6. package/build/css/main.css +1 -1
  7. package/build/css/my-account-main-v2.css +1 -1
  8. package/build/css/my-account-main.css +1 -1
  9. package/build/css/pages/checkout-new.css +1 -1
  10. package/build/css/pages/checkout.css +1 -1
  11. package/build/css/pages/page_my-order.css +1 -1
  12. package/build/css/pages/page_wismo-details.css +1 -1
  13. package/build/css/pdp-main-before-combine.css +1 -1
  14. package/build/css/pdp-main-critical.css +1 -1
  15. package/build/css/pdp-main-non-critical.css +1 -1
  16. package/build/css/pdp-main.css +1 -1
  17. package/build/css/plp-main.css +1 -1
  18. package/build/css/store-locator-main.css +1 -1
  19. package/build/img/payment/checkout/giftcard.svg +28 -0
  20. package/build/img/payment/footer/giftcard.svg +28 -0
  21. package/build/js/account-members.min.js +1 -1
  22. package/build/js/add-project-list-id.min.js +1 -1
  23. package/build/js/address-book.min.js +1 -1
  24. package/build/js/basket.min.js +2 -2
  25. package/build/js/bundle.min.js +1 -1
  26. package/build/js/change-password.min.js +1 -1
  27. package/build/js/checkout.min.js +2 -2
  28. package/build/js/emulation.min.js +424 -82
  29. package/build/js/general.bundle.min.js +1 -1
  30. package/build/js/gift-cards.min.js +1 -1
  31. package/build/js/merged-checkout.min.js +2 -2
  32. package/build/js/page/bloomreach-widget/bloomreach-widget-core.js +1 -3
  33. package/build/js/page/checkout-payment-details.js +114 -10
  34. package/build/js/page/components/gift-cards.js +524 -115
  35. package/build/js/page/components/postcode-lookup/postcode-lookup-api.js +56 -0
  36. package/build/js/page/components/postcode-lookup/postcode-lookup-render.js +98 -0
  37. package/build/js/page/components/postcode-lookup/postcode-lookup-validation.js +81 -0
  38. package/build/js/page/components/postcode-lookup.js +503 -0
  39. package/build/js/page/ripple/bulk-add.js +2 -2
  40. package/build/js/page/ripple/thumb-gallery.js +14 -8
  41. package/build/js/page/utils/address-utils.js +32 -0
  42. package/build/js/page/utils/validation.js +54 -1
  43. package/build/js/pdp.bundle.min.js +2 -2
  44. package/build/js/personal-details.min.js +1 -1
  45. package/build/js/plp.bundle.min.js +1 -1
  46. package/build/js/postcode-checker.min.js +1 -1
  47. package/build/js/project-list.min.js +57 -2
  48. package/build/js/quiz.min.js +1 -1
  49. package/build/js/track-my-order.min.js +1 -1
  50. package/package.json +5 -1
  51. package/src/components/checkout-payment-details-v2.hbs +1 -0
  52. package/src/components/checkout_order-summary-new.hbs +6 -0
  53. package/src/components/checkout_payment-new.hbs +35 -8
  54. package/src/components/gift-cards.hbs +8 -15
  55. package/src/components/order-address.hbs +28 -0
  56. package/src/components/payments.hbs +7 -6
  57. package/src/components/postcode-lookup-v2.hbs +85 -0
  58. package/src/components/wismo-giftcard-payment-item.hbs +29 -0
  59. package/src/components/wismo-payment.hbs +10 -0
  60. package/src/data/data_checkout-giftcards.json +7 -0
  61. package/src/data/data_delivery-address-v2.json +4 -0
  62. package/src/data/data_payments-checkout.json +4 -0
  63. package/src/data/data_postcode-lookup.json +200 -0
  64. package/src/data/data_wismo.json +46 -1
  65. package/src/data/footer_menu.json +4 -0
  66. package/src/elements/input.hbs +3 -1
  67. package/src/img/payment/checkout/giftcard.svg +28 -0
  68. package/src/img/payment/footer/giftcard.svg +28 -0
  69. package/src/js/components/general/footer.js +63 -56
  70. package/src/js/emulation/checkout-payment-details.js +2 -0
  71. package/src/js/emulation/gift-cards.js +22 -0
  72. package/src/js/emulation/mock.js +21 -20
  73. package/src/js/emulation/postcode-checker.js +1 -1
  74. package/src/js/emulation/postcode-lookup.js +122 -0
  75. package/src/js/page/bloomreach-widget/bloomreach-widget-core.js +1 -3
  76. package/src/js/page/checkout-payment-details.js +114 -10
  77. package/src/js/page/components/gift-cards.js +524 -115
  78. package/src/js/page/components/postcode-lookup/postcode-lookup-api.js +56 -0
  79. package/src/js/page/components/postcode-lookup/postcode-lookup-render.js +98 -0
  80. package/src/js/page/components/postcode-lookup/postcode-lookup-validation.js +81 -0
  81. package/src/js/page/components/postcode-lookup.js +503 -0
  82. package/src/js/page/ripple/bulk-add.js +2 -2
  83. package/src/js/page/ripple/thumb-gallery.js +14 -8
  84. package/src/js/page/utils/address-utils.js +32 -0
  85. package/src/js/page/utils/validation.js +54 -1
  86. package/src/page_checkout_confirmation-gift-card-split-payment.html +339 -0
  87. package/src/page_checkout_confirmation-gift-card.html +339 -0
  88. package/src/page_my_account_payment_info_gift_cards.html +96 -0
  89. package/src/page_my_account_payment_info_gift_cards_split_payment.html +96 -0
  90. package/src/page_product-details-billie.html +2 -0
  91. package/src/page_product-details-clearpay.html +1 -0
  92. package/src/scss/common/_CTAs.scss +4 -0
  93. package/src/scss/components/_gift-cards.scss +66 -9
  94. package/src/scss/components/_payments-checkout.scss +2 -2
  95. package/src/scss/components/_payments-v2.scss +5 -7
  96. package/src/scss/components/_postcode-lookup-v2.scss +164 -0
  97. package/src/scss/components/checkout-payment-details-v2.scss +1 -0
  98. package/src/scss/helpers/_CTAs.scss +26 -0
  99. package/src/scss/helpers/_variables.scss +2 -0
  100. package/src/scss/pages/_checkout-confirmation-new.scss +96 -3
  101. package/src/scss/pages/page_wismo-details.scss +29 -0
  102. package/src/sitemap.html +24 -0
@@ -0,0 +1,503 @@
1
+ import {
2
+ bindBlurValidation,
3
+ isFieldRowVisible,
4
+ removeErrorStylingForElement,
5
+ resetBlurValidation,
6
+ } from '../utils/validation';
7
+ import { toCanonicalPostcode } from '../utils/address-utils';
8
+ import { createPostcodeLookupApi } from './postcode-lookup/postcode-lookup-api';
9
+ import {
10
+ MODES,
11
+ autoGrow,
12
+ classes,
13
+ renderDropdown,
14
+ renderHighlight,
15
+ renderMode,
16
+ toggleManualPlaceholders,
17
+ } from './postcode-lookup/postcode-lookup-render';
18
+ import {
19
+ ADDRESS_FIELDS,
20
+ validate,
21
+ validateAddressInputs,
22
+ validateField,
23
+ validateSearch,
24
+ } from './postcode-lookup/postcode-lookup-validation';
25
+
26
+ const DEBOUNCE_MS = 300;
27
+ const MIN_QUERY_LENGTH = 2;
28
+ const EVENT_NS = '.postcodeLookup';
29
+
30
+ const selectors = {
31
+ formRow: '.form-row',
32
+ search: '[data-postcode-lookup-input]',
33
+ line1: '[data-postcode-lookup-line1]',
34
+ line2: '[data-postcode-lookup-line2]',
35
+ town: '[data-postcode-lookup-town]',
36
+ postcode: '[data-postcode-lookup-postcode]',
37
+ line2Wrap: '[data-postcode-lookup-line2-wrap]',
38
+ addLine2: '[data-postcode-lookup-add-line2]',
39
+ searchWrap: '[data-postcode-lookup-search]',
40
+ dropdown: '[data-postcode-lookup-dropdown]',
41
+ list: '[data-postcode-lookup-list]',
42
+ hint: '[data-postcode-lookup-hint]',
43
+ manual: '[data-postcode-lookup-manual]',
44
+ manualBtn: '[data-postcode-lookup-manual-btn]',
45
+ expand: '[data-postcode-lookup-expand]',
46
+ };
47
+
48
+ const api = createPostcodeLookupApi();
49
+
50
+ function setSuggestions(lookup, list) {
51
+ const { state } = lookup;
52
+
53
+ state.suggestions = list;
54
+ state.highlightedIndex = -1;
55
+ }
56
+
57
+ function openDropdown(lookup) {
58
+ const { state, $search } = lookup;
59
+
60
+ if (state.mode === MODES.UNAVAILABLE) return;
61
+
62
+ state.isOpen = true;
63
+ renderDropdown(lookup);
64
+ removeErrorStylingForElement($search);
65
+ }
66
+
67
+ function closeDropdown(lookup, { shouldValidate = true } = {}) {
68
+ const { state } = lookup;
69
+
70
+ if (!state.isOpen) return;
71
+
72
+ state.isOpen = false;
73
+ state.highlightedIndex = -1;
74
+ renderDropdown(lookup);
75
+ updateSearchFromFields(lookup);
76
+ if (shouldValidate) validateSearch(lookup);
77
+ }
78
+
79
+ function setExternalError(lookup) {
80
+ const { state, $search } = lookup;
81
+
82
+ cancelLookup(lookup);
83
+ state.isOpen = false;
84
+ renderDropdown(lookup);
85
+ removeErrorStylingForElement($search);
86
+ setMode(lookup, MODES.UNAVAILABLE);
87
+ }
88
+
89
+ function setMode(lookup, mode) {
90
+ const { state } = lookup;
91
+
92
+ if (state.mode === MODES.UNAVAILABLE) return;
93
+
94
+ state.mode = mode;
95
+ renderMode(lookup);
96
+ }
97
+
98
+ function setSearchValue(lookup, value) {
99
+ const { $search } = lookup;
100
+
101
+ $search.val(value);
102
+ autoGrow(lookup);
103
+ }
104
+
105
+ function cancelLookup(lookup) {
106
+ const { requestIds, debouncedFetch } = lookup;
107
+
108
+ debouncedFetch.cancel();
109
+ requestIds.autocomplete += 1;
110
+ setSuggestions(lookup, []);
111
+ }
112
+
113
+ async function fetchSuggestions(lookup, query) {
114
+ const { requestIds } = lookup;
115
+
116
+ requestIds.autocomplete += 1;
117
+ const currentRequest = requestIds.autocomplete;
118
+
119
+ try {
120
+ const hits = await api.autocomplete(query);
121
+ if (currentRequest !== requestIds.autocomplete) return;
122
+
123
+ setSuggestions(lookup, hits);
124
+ renderDropdown(lookup);
125
+ } catch (e) {
126
+ if (currentRequest !== requestIds.autocomplete) return;
127
+ setExternalError(lookup);
128
+ }
129
+ }
130
+
131
+ function handleInput(lookup) {
132
+ const { $search, debouncedFetch } = lookup;
133
+
134
+ autoGrow(lookup);
135
+
136
+ const query = ($search.val() || '').trim();
137
+
138
+ if (query.length < MIN_QUERY_LENGTH) {
139
+ cancelLookup(lookup);
140
+ } else {
141
+ debouncedFetch(query);
142
+ }
143
+
144
+ openDropdown(lookup);
145
+ }
146
+
147
+ function hasVisibleSuggestions(lookup) {
148
+ const { state, $search } = lookup;
149
+
150
+ return state.isOpen && !!$search.val() && state.suggestions.length > 0;
151
+ }
152
+
153
+ function navigate(lookup, step) {
154
+ const { state } = lookup;
155
+
156
+ if (!hasVisibleSuggestions(lookup)) return;
157
+
158
+ const totalItems = state.suggestions.length;
159
+ const isFirstMoveUp = state.highlightedIndex === -1 && step < 0;
160
+ const nextIndex = isFirstMoveUp ? totalItems - 1 : state.highlightedIndex + step;
161
+
162
+ state.highlightedIndex = (nextIndex + totalItems) % totalItems;
163
+ renderHighlight(lookup);
164
+ scrollToHighlighted(lookup);
165
+ }
166
+
167
+ function selectHighlighted(lookup) {
168
+ const { state } = lookup;
169
+
170
+ if (state.highlightedIndex === -1 || !hasVisibleSuggestions(lookup)) return;
171
+
172
+ const selected = state.suggestions[state.highlightedIndex];
173
+ if (selected) selectSuggestion(lookup, selected);
174
+ }
175
+
176
+ function scrollToHighlighted(lookup) {
177
+ const { state, $list, $manual } = lookup;
178
+
179
+ if (!state.isOpen) return;
180
+
181
+ const highlightedEl = $list.find('.' + classes.highlighted)[0];
182
+ if (!highlightedEl) return;
183
+
184
+ $list[0].style.scrollPaddingBottom = `${$manual[0].offsetHeight}px`;
185
+ highlightedEl.scrollIntoView({ block: 'nearest' });
186
+ }
187
+
188
+ function handleEsc(lookup) {
189
+ const { state } = lookup;
190
+
191
+ state.isOpen = true;
192
+ resetAddressSearch(lookup);
193
+ renderDropdown(lookup);
194
+ }
195
+
196
+ function handleSearchKeydown(lookup, e) {
197
+ switch (e.key) {
198
+ case 'ArrowDown':
199
+ case 'Down':
200
+ e.preventDefault();
201
+ navigate(lookup, 1);
202
+ break;
203
+ case 'ArrowUp':
204
+ case 'Up':
205
+ e.preventDefault();
206
+ navigate(lookup, -1);
207
+ break;
208
+ case 'Enter':
209
+ e.preventDefault();
210
+ selectHighlighted(lookup);
211
+ break;
212
+ case 'Escape':
213
+ case 'Esc':
214
+ e.preventDefault();
215
+ handleEsc(lookup);
216
+ break;
217
+ }
218
+ }
219
+
220
+ function showLine2(lookup) {
221
+ const { $line2Wrap, $addLine2 } = lookup;
222
+
223
+ $line2Wrap.removeClass(classes.hidden);
224
+ $addLine2.addClass(classes.hidden);
225
+ }
226
+
227
+ function resetAddressSearch(lookup) {
228
+ cancelLookup(lookup);
229
+ setSearchValue(lookup, '');
230
+ }
231
+
232
+ function resetAddressInputs(lookup) {
233
+ const { $fields } = lookup;
234
+
235
+ ADDRESS_FIELDS.forEach((key) => {
236
+ const $field = $fields[key];
237
+ $field.val('');
238
+ removeErrorStylingForElement($field);
239
+ resetBlurValidation($field);
240
+ });
241
+ }
242
+
243
+ function enterManualMode(lookup) {
244
+ const { state, requestIds } = lookup;
245
+
246
+ requestIds.resolve += 1;
247
+ resetAddressSearch(lookup);
248
+ state.isOpen = false;
249
+ renderDropdown(lookup);
250
+
251
+ setMode(lookup, MODES.MANUAL);
252
+ toggleManualPlaceholders(lookup, true);
253
+ resetAddressInputs(lookup);
254
+ }
255
+
256
+ function exitManualMode(lookup) {
257
+ const { $search } = lookup;
258
+
259
+ setMode(lookup, MODES.LOOKUP);
260
+ toggleManualPlaceholders(lookup, false);
261
+ resetAddressSearch(lookup);
262
+ removeErrorStylingForElement($search);
263
+ resetAddressInputs(lookup);
264
+ }
265
+
266
+ function reset(lookup) {
267
+ const { state, requestIds, $search } = lookup;
268
+
269
+ requestIds.resolve += 1;
270
+ closeDropdown(lookup, { shouldValidate: false });
271
+ setMode(lookup, MODES.LOOKUP);
272
+ toggleManualPlaceholders(lookup, false);
273
+ resetAddressSearch(lookup);
274
+ removeErrorStylingForElement($search);
275
+ resetBlurValidation($search);
276
+ resetAddressInputs(lookup);
277
+ state.isValidationPending = false;
278
+ }
279
+
280
+ function setShown(lookup, isShown) {
281
+ const { state } = lookup;
282
+
283
+ if (!isShown) {
284
+ if (state.isOpen) {
285
+ cancelLookup(lookup);
286
+ closeDropdown(lookup, { shouldValidate: false });
287
+ }
288
+ return;
289
+ }
290
+
291
+ autoGrow(lookup);
292
+
293
+ if (state.isValidationPending) {
294
+ state.isValidationPending = false;
295
+ validateSearch(lookup);
296
+ validateAddressInputs(lookup);
297
+ }
298
+ }
299
+
300
+ function fillAddress(lookup, address) {
301
+ const { state, $search, $fields } = lookup;
302
+ const values = {
303
+ line1: address.line1,
304
+ line2: address.line2,
305
+ town: address.town,
306
+ postcode: toCanonicalPostcode(address.postcode),
307
+ };
308
+
309
+ if (values.line2) showLine2(lookup);
310
+
311
+ Object.keys(values).forEach((key) => {
312
+ const value = key === 'postcode' ? values[key] : values[key].trim();
313
+ $fields[key].val(value);
314
+ });
315
+
316
+ updateSearchFromFields(lookup);
317
+ renderDropdown(lookup);
318
+ validateSearch(lookup);
319
+ validateAddressInputs(lookup);
320
+ state.isValidationPending = !isFieldRowVisible($search);
321
+ }
322
+
323
+ function updateSearchFromFields(lookup) {
324
+ const { $fields } = lookup;
325
+ const combined = ADDRESS_FIELDS.map((key) => $fields[key].val())
326
+ .filter(Boolean)
327
+ .join(', ');
328
+
329
+ setSearchValue(lookup, combined);
330
+ }
331
+
332
+ function handleFieldChange(lookup, key) {
333
+ const { $fields } = lookup;
334
+
335
+ if (key === 'postcode') {
336
+ const $postcode = $fields.postcode;
337
+ $postcode.val(toCanonicalPostcode($postcode.val()));
338
+ }
339
+ updateSearchFromFields(lookup);
340
+ validateSearch(lookup);
341
+ }
342
+
343
+ async function selectSuggestion(lookup, suggestion) {
344
+ const { state, requestIds, $search } = lookup;
345
+
346
+ state.isPopulating = true;
347
+ if (document.activeElement !== $search[0]) $search[0].focus();
348
+ cancelLookup(lookup);
349
+ closeDropdown(lookup, { shouldValidate: false });
350
+
351
+ requestIds.resolve += 1;
352
+ const currentResolve = requestIds.resolve;
353
+
354
+ try {
355
+ const address = await api.resolve(suggestion);
356
+ state.isPopulating = false;
357
+ if (currentResolve !== requestIds.resolve) return;
358
+ if (!address) throw new Error('Address not found');
359
+
360
+ fillAddress(lookup, address);
361
+ } catch (e) {
362
+ state.isPopulating = false;
363
+ if (currentResolve !== requestIds.resolve) return;
364
+ setExternalError(lookup);
365
+ }
366
+ }
367
+
368
+ async function checkApiKey(lookup) {
369
+ try {
370
+ const { available } = await api.checkKey();
371
+ if (!available) {
372
+ setExternalError(lookup);
373
+ }
374
+ } catch (e) {
375
+ setExternalError(lookup);
376
+ }
377
+ }
378
+
379
+ function bindEvents(lookup) {
380
+ const {
381
+ rootSelector,
382
+ state,
383
+ $search,
384
+ $searchLabel,
385
+ $searchWrap,
386
+ $dropdown,
387
+ $list,
388
+ $hint,
389
+ $manual,
390
+ $expand,
391
+ $addLine2,
392
+ } = lookup;
393
+
394
+ $search.on('input', () => handleInput(lookup));
395
+ $search.on('keydown', (e) => handleSearchKeydown(lookup, e));
396
+
397
+ ADDRESS_FIELDS.forEach((key) => {
398
+ bindBlurValidation(`${rootSelector} ${selectors[key]}`, () => validateField(lookup, key), {
399
+ namespace: EVENT_NS,
400
+ onChange: () => handleFieldChange(lookup, key),
401
+ });
402
+ });
403
+
404
+ $search.on('focus', () => openDropdown(lookup));
405
+
406
+ $addLine2.on('click', () => showLine2(lookup));
407
+
408
+ $list.on('mousedown', `.${classes.item}, ${selectors.manual}`, function (e) {
409
+ e.preventDefault();
410
+ });
411
+
412
+ $list.on('click', '.' + classes.item, function () {
413
+ const suggestion = state.suggestions[$(this).data('index')];
414
+ if (suggestion) selectSuggestion(lookup, suggestion);
415
+ });
416
+
417
+ $manual.on('click', selectors.manualBtn, function (e) {
418
+ e.stopPropagation();
419
+ enterManualMode(lookup);
420
+ });
421
+
422
+ $expand.on('click', () => exitManualMode(lookup));
423
+
424
+ $hint.on('pointerdown', () => closeDropdown(lookup));
425
+
426
+ $(document).on(`pointerdown${EVENT_NS}`, function (e) {
427
+ if (!state.isOpen) {
428
+ if (state.isPopulating && !$(e.target).closest($search).length) validateSearch(lookup);
429
+ return;
430
+ }
431
+ if (!$(e.target).closest($search.add($dropdown)).length) closeDropdown(lookup);
432
+ });
433
+
434
+ $searchLabel.on('click', function (e) {
435
+ e.preventDefault();
436
+ });
437
+
438
+ $searchWrap.on('focusout', function (e) {
439
+ if (!state.isOpen) {
440
+ if (state.isPopulating) validateSearch(lookup);
441
+ return;
442
+ }
443
+ if (!e.relatedTarget) return;
444
+ if (!$.contains(this, e.relatedTarget)) closeDropdown(lookup);
445
+ });
446
+
447
+ $(window).on(`blur${EVENT_NS}`, () => closeDropdown(lookup));
448
+ }
449
+
450
+ export function createPostcodeLookup(context) {
451
+ const rootSelector = `[data-postcode-lookup="${context}"]`;
452
+ const $root = $(rootSelector);
453
+ const find = (key) => $root.find(selectors[key]);
454
+
455
+ const $search = find('search');
456
+ if (!$search.length) return null;
457
+
458
+ const lookup = {
459
+ rootSelector,
460
+ $search,
461
+ $searchLabel: $search.closest(selectors.formRow).find('label'),
462
+ $fields: {
463
+ line1: find('line1'),
464
+ line2: find('line2'),
465
+ town: find('town'),
466
+ postcode: find('postcode'),
467
+ },
468
+ $line2Wrap: find('line2Wrap'),
469
+ $addLine2: find('addLine2'),
470
+ $searchWrap: find('searchWrap'),
471
+ $dropdown: find('dropdown'),
472
+ $list: find('list'),
473
+ $hint: find('hint'),
474
+ $manual: find('manual'),
475
+ $expand: find('expand'),
476
+ state: {
477
+ mode: MODES.LOOKUP,
478
+ isOpen: false,
479
+ isPopulating: false,
480
+ isValidationPending: false,
481
+ suggestions: [],
482
+ highlightedIndex: -1,
483
+ },
484
+ requestIds: {
485
+ autocomplete: 0,
486
+ resolve: 0,
487
+ },
488
+ };
489
+
490
+ lookup.debouncedFetch = _.debounce((query) => fetchSuggestions(lookup, query), DEBOUNCE_MS);
491
+
492
+ bindEvents(lookup);
493
+
494
+ if (lookup.$fields.line2.val()) showLine2(lookup);
495
+ checkApiKey(lookup);
496
+ autoGrow(lookup);
497
+
498
+ return {
499
+ validate: (options) => validate(lookup, options),
500
+ setShown: (isShown) => setShown(lookup, isShown),
501
+ reset: () => reset(lookup),
502
+ };
503
+ }
@@ -29,9 +29,9 @@ export function createBulkAddHelper({ productCodeSelector }) {
29
29
 
30
30
  function addProductToCart(postcode) {
31
31
  const item = { productCode: getProductCode(), quantity: QUANTITY };
32
-
32
+
33
33
  const payload = {
34
- entries: [item]
34
+ entries: [item],
35
35
  };
36
36
 
37
37
  if (postcode) {
@@ -39,9 +39,8 @@ Wick.ThumbGallery = (function () {
39
39
  return [];
40
40
  }
41
41
  const trimmed = hybrisString.trim();
42
- const content = trimmed.startsWith('[') && trimmed.endsWith(']')
43
- ? trimmed.slice(1, -1)
44
- : trimmed;
42
+ const content =
43
+ trimmed.startsWith('[') && trimmed.endsWith(']') ? trimmed.slice(1, -1) : trimmed;
45
44
 
46
45
  return content
47
46
  .split(',')
@@ -68,7 +67,8 @@ Wick.ThumbGallery = (function () {
68
67
 
69
68
  function generateCloudinaryMainSlide(publicId, baseUrl) {
70
69
  const sourcesHtml = IMG_BREAKPOINTS.map(
71
- (bp) => `<source srcset="${buildCloudinaryUrl(publicId, bp.w, bp.h, baseUrl)}" media="${bp.media}">`
70
+ (bp) =>
71
+ `<source srcset="${buildCloudinaryUrl(publicId, bp.w, bp.h, baseUrl)}" media="${bp.media}">`
72
72
  ).join('\n ');
73
73
  const fallbackSrc = buildCloudinaryUrl(publicId, MAIN_DEFAULT.w, MAIN_DEFAULT.h, baseUrl);
74
74
  return renderMainSlide(fallbackSrc, sourcesHtml);
@@ -208,9 +208,15 @@ Wick.ThumbGallery = (function () {
208
208
 
209
209
  const swiper = new Swiper(
210
210
  $container[0],
211
- getMainSwiperConfig($nextBtn, $prevBtn, thumbsSwiper, () => {
212
- updateActiveThumbnail(swiper, thumbsSwiper);
213
- }, total)
211
+ getMainSwiperConfig(
212
+ $nextBtn,
213
+ $prevBtn,
214
+ thumbsSwiper,
215
+ () => {
216
+ updateActiveThumbnail(swiper, thumbsSwiper);
217
+ },
218
+ total
219
+ )
214
220
  );
215
221
 
216
222
  $thumbContainer
@@ -223,7 +229,7 @@ Wick.ThumbGallery = (function () {
223
229
  function init(hybrisString, options = {}) {
224
230
  const ids = parseIdentifiers(hybrisString);
225
231
 
226
- const opts = typeof options === 'string' ? { mediaDomain: options } : (options || {});
232
+ const opts = typeof options === 'string' ? { mediaDomain: options } : options || {};
227
233
  const baseUrl = getBaseUrl(opts.mediaDomain);
228
234
  const missingImage = opts.missingImage || '';
229
235
 
@@ -0,0 +1,32 @@
1
+ export function getAddressLines(address) {
2
+ const { line_1: line1, line_2: line2, line_3: line3 } = address;
3
+
4
+ if (line3) {
5
+ return {
6
+ line1: [line1, line2].filter(Boolean).join(', '),
7
+ line2: line3,
8
+ };
9
+ }
10
+
11
+ return { line1, line2 };
12
+ }
13
+
14
+ export function toCanonicalPostcode(postcode) {
15
+ const compact = String(postcode || '')
16
+ .toUpperCase()
17
+ .replace(/\s/g, '');
18
+
19
+ if (compact.length < 3) return compact;
20
+ return compact.slice(0, -3) + ' ' + compact.slice(-3);
21
+ }
22
+
23
+ export function resolvePostcodeToAddress(result) {
24
+ const { line1, line2 } = getAddressLines(result);
25
+
26
+ return {
27
+ line1: line1 || '',
28
+ line2: line2 || '',
29
+ town: result.post_town || '',
30
+ postcode: result.postcode || '',
31
+ };
32
+ }
@@ -12,10 +12,22 @@ const formElements = {
12
12
  };
13
13
 
14
14
  const patterns = {
15
+ antiXssPattern: new RegExp("^[\\w()/&.,'`-]+[\\w ()/&.,'`-]*[\\w()/&.,'`-]+$|^[\\w()/&.,'`-]$"),
15
16
  guidPattern: /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i,
16
17
  userFriendlyIdPattern: /^[A-Za-z]{2,3}\d{6}$/,
18
+ lookupPostcodePattern: new RegExp(
19
+ '^(GIR 0AA)$|^((([A-PR-UWYZ][0-9][0-9]?)|([A-PR-UWYZ][A-HK-Y][0-9][0-9]?)|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY])) [0-9][A-BD-HJLNP-UW-Z]{2})$'
20
+ ),
17
21
  };
18
22
 
23
+ export function isValidLookupPostcode(value) {
24
+ return patterns.lookupPostcodePattern.test(value);
25
+ }
26
+
27
+ export function isAntiXssValid(value) {
28
+ return patterns.antiXssPattern.test(value);
29
+ }
30
+
19
31
  export function addFieldValidation(fieldSelector, validationFunction) {
20
32
  $(fieldSelector).each(function (index, element) {
21
33
  $(element).blur(function () {
@@ -244,6 +256,14 @@ export function removeErrorStyling(element) {
244
256
  element.find('.form-row__error').remove();
245
257
  }
246
258
 
259
+ export function removeErrorStylingForElement(element) {
260
+ removeErrorStyling($(element).closest('.form-row'));
261
+ }
262
+
263
+ export function isFieldRowVisible(element) {
264
+ return $(element).closest('.form-row').is(':visible');
265
+ }
266
+
247
267
  export function wrapWithError(element) {
248
268
  $(element).addClass('form-row_validation-error');
249
269
  }
@@ -277,7 +297,7 @@ export function showValidationErrors(form, errorMsg) {
277
297
  }
278
298
  }
279
299
 
280
- function isElementInViewport(el) {
300
+ export function isElementInViewport(el) {
281
301
  if (typeof jQuery === 'function' && el instanceof jQuery) {
282
302
  el = el[0];
283
303
  }
@@ -366,3 +386,36 @@ function validateByPredicate(
366
386
  export function validateExactDigits($input, n, message, cfg) {
367
387
  return validateByPredicate($input, (v) => hasExactDigits(v, n), message, cfg);
368
388
  }
389
+
390
+ export function validateFieldByRule($field, { required, validate, message }) {
391
+ if (!$field.length || !isFieldRowVisible($field)) return true;
392
+
393
+ const value = ($field.val() || '').trim();
394
+ const isValid = value ? validate(value) : !required;
395
+
396
+ updateStylingErrorForElement($field, isValid, message);
397
+ return isValid;
398
+ }
399
+
400
+ const blurredFields = new WeakSet();
401
+
402
+ export function bindBlurValidation(selector, validate, { namespace = '', onChange } = {}) {
403
+ $(document)
404
+ .on(`change${namespace}`, selector, function () {
405
+ updateInputValueBeforeValidation(this);
406
+ validate(this);
407
+ if (onChange) onChange(this);
408
+ })
409
+ .on(`blur${namespace}`, selector, function () {
410
+ blurredFields.add(this);
411
+ updateInputValueBeforeValidation(this);
412
+ validate(this);
413
+ })
414
+ .on(`input${namespace}`, selector, function () {
415
+ if (blurredFields.has(this)) validate(this);
416
+ });
417
+ }
418
+
419
+ export function resetBlurValidation(field) {
420
+ $(field).each((_, el) => blurredFields.delete(el));
421
+ }