@iamproperty/components 3.7.9-beta-2 → 3.8.0

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 (48) hide show
  1. package/assets/css/components/accordion.css.map +1 -1
  2. package/assets/css/components/dialog.css +1 -1
  3. package/assets/css/components/dialog.css.map +1 -1
  4. package/assets/css/components/fileupload.css.map +1 -1
  5. package/assets/css/components/forms.css +1 -1
  6. package/assets/css/components/forms.css.map +1 -1
  7. package/assets/css/components/header.css +1 -1
  8. package/assets/css/components/header.css.map +1 -1
  9. package/assets/css/components/lists.css.map +1 -1
  10. package/assets/css/components/nav.css +1 -1
  11. package/assets/css/components/nav.css.map +1 -1
  12. package/assets/css/components/property-searchbar.css +1 -1
  13. package/assets/css/components/property-searchbar.css.map +1 -1
  14. package/assets/css/core.min.css +1 -1
  15. package/assets/css/core.min.css.map +1 -1
  16. package/assets/css/style.min.css +1 -1
  17. package/assets/css/style.min.css.map +1 -1
  18. package/assets/js/components/accordion/accordion.component.min.js +1 -1
  19. package/assets/js/components/applied-filters/applied-filters.component.min.js +1 -1
  20. package/assets/js/components/card/card.component.min.js +1 -1
  21. package/assets/js/components/filterlist/filterlist.component.min.js +1 -1
  22. package/assets/js/components/header/header.component.min.js +2 -2
  23. package/assets/js/components/notification/notification.component.min.js +1 -1
  24. package/assets/js/components/pagination/pagination.component.min.js +1 -1
  25. package/assets/js/components/table/table.component.min.js +12 -12
  26. package/assets/js/components/table/table.component.min.js.map +1 -1
  27. package/assets/js/components/tabs/tabs.component.min.js +1 -1
  28. package/assets/js/dynamic.min.js +5 -5
  29. package/assets/js/dynamic.min.js.map +1 -1
  30. package/assets/js/modules/dialogs.js +109 -24
  31. package/assets/js/modules/table.js +23 -5
  32. package/assets/js/scripts.bundle.js +22 -22
  33. package/assets/js/scripts.bundle.js.map +1 -1
  34. package/assets/js/scripts.bundle.min.js +2 -2
  35. package/assets/js/scripts.bundle.min.js.map +1 -1
  36. package/assets/js/tests/filterlist.spec.js +1 -1
  37. package/assets/sass/_functions/variables.scss +4 -2
  38. package/assets/sass/components/dialog.scss +156 -5
  39. package/assets/sass/components/forms.scss +67 -21
  40. package/assets/sass/foundations/buttons.scss +48 -0
  41. package/assets/ts/modules/dialogs.ts +156 -29
  42. package/assets/ts/modules/table.ts +29 -7
  43. package/assets/ts/tests/dialogs.spec.js +50 -0
  44. package/assets/ts/tests/filterlist.spec.ts +1 -1
  45. package/dist/components.es.js +838 -830
  46. package/dist/components.umd.js +28 -28
  47. package/dist/style.css +1 -1
  48. package/package.json +1 -1
@@ -3,6 +3,20 @@ import { createEmbed } from "./youtubevideo";
3
3
 
4
4
  const extendDialogs = (body) => {
5
5
 
6
+ Array.from(body.querySelectorAll('dialog[open]')).forEach((dialog, index) => {
7
+
8
+ let parent = dialog.closest('.dialog__wrapper');
9
+
10
+ if(!parent){
11
+
12
+ dialog.removeAttribute('open');
13
+ dialog.showModal();
14
+ dialog.focus();
15
+
16
+ createDialog(dialog);
17
+ }
18
+ });
19
+
6
20
  // Dialogs/modals
7
21
  body.addEventListener('click', (event) => {
8
22
 
@@ -15,20 +29,6 @@ const extendDialogs = (body) => {
15
29
 
16
30
  createDialog(dialog);
17
31
 
18
-
19
- // Prevent the user from escaping the model when transactional
20
- if(dialog.querySelector(':scope > .mh-lg > form:last-child > button:last-child, :scope > .mh-lg > button:last-child') && !dialog.classList.contains('dialog--multi')) {
21
- dialog.addEventListener("cancel", (e) => {
22
- e.preventDefault();
23
- });
24
- }
25
-
26
- // Create the video embed
27
- let videoButton = dialog.querySelector('.youtube-embed a');
28
- if (videoButton){
29
- createEmbed(videoButton)
30
- }
31
-
32
32
  // Open the modal!
33
33
  dialog.showModal();
34
34
  dialog.focus();
@@ -77,7 +77,12 @@ const extendDialogs = (body) => {
77
77
 
78
78
  // Close the modal when clicked on the backdrop
79
79
  if (event && event.target instanceof HTMLElement && event.target.closest('dialog[open]')){
80
- const dialog = event.target.closest('dialog[open]');
80
+ let dialog = event.target.closest('dialog[open]');
81
+
82
+ // Small fix to make sure the dialog isn't a dialog inside of a dialog.
83
+ var style = window.getComputedStyle(dialog);
84
+ if(style.display === 'contents')
85
+ dialog = dialog.parentNode.closest('dialog[open]');
81
86
 
82
87
  // Dont allow the backdrop to be clicked when transactional
83
88
  if(!dialog.querySelector(':scope > .mh-lg > form:last-child > button:last-child, :scope > .mh-lg > button:last-child') || dialog.classList.contains('dialog--multi')){
@@ -106,8 +111,8 @@ const extendDialogs = (body) => {
106
111
  let dataEvent = "openPopover"
107
112
  let popover = parent.querySelector(':scope > dialog');
108
113
 
109
- if(document.querySelector('dialog[open]') && document.querySelector('dialog[open]') != popover)
110
- document.querySelector('dialog[open]').close();
114
+ if(document.querySelector('*:not([data-keep-open]) > dialog[open]') && document.querySelector('*:not([data-keep-open]) > dialog[open]') != popover)
115
+ document.querySelector('*:not([data-keep-open]) > dialog[open]').close();
111
116
 
112
117
  // Remove active class from exiting active buttons
113
118
  Array.from(document.querySelectorAll('.dialog__wrapper > button')).forEach((btnElement,index) => {
@@ -152,8 +157,14 @@ const extendDialogs = (body) => {
152
157
  if(popoverBottom > windowPos){
153
158
 
154
159
  let currentStyle = popover.hasAttribute('style') ? popover.getAttribute('style')+' ' : '';
155
-
156
160
  popover.setAttribute('style',currentStyle+`transform: translate(0, calc(-100% - 4rem))`);
161
+
162
+ // Check that the dialog doesn't go over the top of the page
163
+ boundingRec = popover.getBoundingClientRect();
164
+ let popoverTop = boundingRec.top - window.scrollY;
165
+
166
+ if(popoverTop < 100)
167
+ popover.removeAttribute('style');
157
168
  }
158
169
 
159
170
  window.dataLayer = window.dataLayer || [];
@@ -181,11 +192,25 @@ const extendDialogs = (body) => {
181
192
 
182
193
  export const createDialog = (dialog) => {
183
194
 
195
+ // Prevent the user from escaping the model when transactional
196
+ if(dialog.querySelector(':scope > .mh-lg > form:last-child > button:last-child, :scope > .mh-lg > button:last-child') && !dialog.classList.contains('dialog--multi')) {
197
+ dialog.addEventListener("cancel", (e) => {
198
+ e.preventDefault();
199
+ });
200
+ }
201
+
202
+ // Create the video embed
203
+ let videoButton = dialog.querySelector('.youtube-embed a');
204
+ if (videoButton){
205
+ createEmbed(videoButton)
206
+ }
207
+
184
208
  // Multi dialog
185
209
  if(dialog.classList.contains('dialog--multi') && !dialog.querySelector(':scope > .steps')) {
186
210
  createMultiFormDialog(dialog);
187
211
  }
188
212
 
213
+ // If you are using Vue eevents and bindings its recommended to add in the .mh-lg div manually to the dialog
189
214
  if(!dialog.querySelector(':scope > .mh-lg') && !dialog.classList.contains('dialog--multi')){
190
215
  dialog.innerHTML = `<div class="mh-lg">${dialog.innerHTML}</div>`;
191
216
 
@@ -205,37 +230,101 @@ export const createDialog = (dialog) => {
205
230
 
206
231
  // Create close button is needed
207
232
  if(!dialog.querySelector(':scope > button:first-child'))
208
- dialog.innerHTML = `<button class="dialog__close">Close</button>${dialog.innerHTML}`;
233
+ dialog.insertAdjacentHTML('afterbegin', `<button class="dialog__close">Close</button>`);
209
234
 
210
235
  }
211
236
 
212
- const createMultiFormDialog = (dialog) => {
237
+ export const createMultiFormDialog = (dialog) => {
238
+
213
239
  let buttons = "";
214
240
  let fieldsets = Array.from(dialog.querySelectorAll('fieldset[data-title]'));
241
+
215
242
  fieldsets.forEach((fieldset,index) => {
216
- buttons += `<button data-title="${fieldset.getAttribute('data-title')}" type="button" class="${index == 0 ? "active":""}">${fieldset.getAttribute('data-title')}</button>`;
243
+ buttons += `<button data-title="${fieldset.getAttribute('data-title')}" type="button" class="${index == 0 ? "active":""}" tabindex="-1">${fieldset.getAttribute('data-title')}</button>`;
217
244
 
218
245
  const btnWrapper = document.createElement("div");
219
246
  btnWrapper.classList.add('btn--wrapper');
220
247
  fieldset.appendChild(btnWrapper);
221
248
 
222
-
223
249
  if(index != 0)
224
- btnWrapper.innerHTML += `<button data-title="${fieldsets[index-1].getAttribute('data-title')}" class="btn btn-secondary mb-0" type="button">Previous</button>`;
250
+ btnWrapper.innerHTML += `<button data-title="${fieldsets[index-1].getAttribute('data-title')}" class="btn btn-secondary mb-0" data-previous type="button">Previous</button>`;
225
251
 
226
252
  if(index != fieldsets.length - 1)
227
- btnWrapper.innerHTML += `<button data-title="${fieldsets[index+1].getAttribute('data-title')}" class="btn btn-primary mb-0" type="button">Next</button>`;
253
+ btnWrapper.innerHTML += `<button data-title="${fieldsets[index+1].getAttribute('data-title')}" class="btn btn-primary mb-0" data-next type="button">Next</button>`;
228
254
 
229
255
  if(index == fieldsets.length - 1)
230
- btnWrapper.innerHTML += `<button class="btn btn-primary mb-0">Submit</button>`;
256
+ btnWrapper.innerHTML += `<button data-title="${fieldsets[index].getAttribute('data-title')}" class="btn btn-primary mb-0" data-next type="submit">Submit</button>`;
231
257
  });
232
258
 
233
- dialog.innerHTML = `<div class="steps bg-primary">${buttons}</div>${dialog.innerHTML}`;
259
+ dialog.insertAdjacentHTML('afterbegin',`<div class="steps bg-primary">${buttons}</div>`);
234
260
 
235
261
 
236
- dialog.addEventListener('click', (event) => {
237
- if (event && event.target instanceof HTMLElement && event.target.closest('button[data-title]')){
238
- const button = event.target.closest('button[data-title]');
262
+ // Open the fieldset with an error inside
263
+ let validatedFieldsets = Array.from(dialog.querySelectorAll('fieldset.was-validated'));
264
+ for (let i = 0; i < validatedFieldsets.length; i++) {
265
+
266
+ let fieldset = validatedFieldsets[i];
267
+ let fieldsetID = fieldset.getAttribute('data-title');
268
+
269
+ if(fieldset.querySelector('.is-invalid')){
270
+
271
+ Array.from(dialog.querySelectorAll(`[data-title="${fieldsetID}"]`)).forEach((element, index) => {
272
+
273
+ element.classList.add('active');
274
+ });
275
+
276
+ break;
277
+ }
278
+ else {
279
+
280
+ Array.from(dialog.querySelectorAll(`[data-title="${fieldsetID}"]`)).forEach((element, index) => {
281
+
282
+ element.classList.add('valid');
283
+ });
284
+ }
285
+ }
286
+
287
+ // Prevent the bubble messages
288
+ dialog.addEventListener('invalid', (function () {
289
+ return function (e) {
290
+ e.preventDefault();
291
+ };
292
+ })(), true);
293
+
294
+
295
+ function validateFieldset(button){
296
+
297
+ const currentFieldset = dialog.querySelector(`fieldset.active`) ? dialog.querySelector(`fieldset.active`) : dialog.querySelector(`fieldset[data-title]`);
298
+ const currentFieldsetID = currentFieldset.getAttribute('data-title');
299
+ let isFieldsetValid = true;
300
+
301
+ currentFieldset.classList.add('was-validated');
302
+
303
+ Array.from(currentFieldset.querySelectorAll('input')).forEach((input, index) => {
304
+
305
+ if (!input.checkValidity())
306
+ isFieldsetValid = false;
307
+ });
308
+
309
+ // If valid mode to next field set
310
+ if(!isFieldsetValid){
311
+
312
+ Array.from(dialog.querySelectorAll(`[data-title="${currentFieldsetID}"]`)).forEach((element, index) => {
313
+
314
+ element.classList.remove('valid');
315
+ });
316
+ }
317
+ else {
318
+
319
+ Array.from(dialog.querySelectorAll(`[data-title="${currentFieldsetID}"]`)).forEach((element, index) => {
320
+
321
+ element.classList.add('valid');
322
+ });
323
+ }
324
+
325
+ // Allow the previous button to navigate
326
+ if(isFieldsetValid || !button.hasAttribute('data-next')){
327
+
239
328
  const fieldset = dialog.querySelector(`fieldset[data-title="${button.getAttribute('data-title')}"]`);
240
329
  const step = dialog.querySelector(`.steps button[data-title="${button.getAttribute('data-title')}"]`);
241
330
 
@@ -248,6 +337,44 @@ const createMultiFormDialog = (dialog) => {
248
337
 
249
338
  step.classList.add('active');
250
339
  fieldset.classList.add('active');
340
+ }
341
+
342
+
343
+ let fieldsetCount = Array.from(dialog.querySelectorAll(`fieldset`)).length;
344
+ let validFieldsetCount = Array.from(dialog.querySelectorAll(`fieldset.valid`)).length;
345
+
346
+ // update the progress bar
347
+ dialog.style.setProperty('--progress', `${(validFieldsetCount/(fieldsetCount - 1) * 100)}%`);
348
+ }
349
+
350
+ // remove error messages from server
351
+ dialog.addEventListener('keydown', (event) => {
352
+ if (event && event.target instanceof HTMLElement && event.target.closest('button')){
353
+
354
+ const button = event.target.closest('button');
355
+
356
+ if(event.keyCode == 13){
357
+
358
+ event.preventDefault();
359
+ validateFieldset(button);
360
+ }
361
+ }
362
+
363
+ if (event && event.target instanceof HTMLElement && event.target.closest('input')){
364
+ const input = event.target.closest('input');
365
+
366
+ input.classList.remove('is-invalid');
367
+
368
+
369
+ }
370
+ });
371
+
372
+
373
+ dialog.addEventListener('click', (event) => {
374
+ if (event && event.target instanceof HTMLElement && event.target.closest('button[data-title]')){
375
+
376
+ const button = event.target.closest('button[data-title]');
377
+ validateFieldset(button);
251
378
  };
252
379
  return null
253
380
  });
@@ -453,15 +453,26 @@ export const filterTable = (table, form, wrapper) => {
453
453
  });
454
454
  }
455
455
 
456
- //Display the filter count
456
+ // Display the filter count
457
457
  Array.from(form.querySelectorAll('[data-filter-count]')).forEach((element, index) => {
458
458
  element.innerHTML = '';
459
+ element.parentNode.classList.remove('hover');
459
460
  });
460
461
 
461
- if(Object.keys(filters).length) {
462
-
462
+ let filterCount = 0;
463
+ Object.values(filters).forEach((filter, index) => {
464
+
465
+ if(typeof filter == "object" && Object.values(filter).length)
466
+ filterCount += Object.values(filter).length;
467
+ else
468
+ filterCount++;
469
+ });
470
+
471
+ if(filterCount) {
472
+
463
473
  Array.from(form.querySelectorAll('[data-filter-count]')).forEach((element, index) => {
464
- element.innerHTML += `(${Object.keys(filters).length})`;
474
+ element.innerHTML += `(${filterCount})`;
475
+ element.parentNode.classList.add('hover');
465
476
  });
466
477
  }
467
478
 
@@ -829,12 +840,23 @@ export const loadAjaxTable = async function (table, form, pagination, wrapper){
829
840
 
830
841
  Array.from(form.querySelectorAll('[data-filter-count]')).forEach((element, index) => {
831
842
  element.innerHTML = '';
843
+ element.parentNode.classList.remove('hover');
832
844
  });
833
845
 
834
- if(Object.keys(filters).length) {
835
-
846
+ let filterCount = 0;
847
+ Object.values(filters).forEach((filter, index) => {
848
+
849
+ if(typeof filter == "object" && Object.values(filter).length)
850
+ filterCount += Object.values(filter).length;
851
+ else
852
+ filterCount++;
853
+ });
854
+
855
+ if(filterCount) {
856
+
836
857
  Array.from(form.querySelectorAll('[data-filter-count]')).forEach((element, index) => {
837
- element.innerHTML += `(${Object.keys(filters).length})`;
858
+ element.innerHTML += `(${filterCount})`;
859
+ element.parentNode.classList.add('hover');
838
860
  });
839
861
  }
840
862
 
@@ -0,0 +1,50 @@
1
+ // @ts-nocheck
2
+ import '@testing-library/jest-dom'
3
+ import { createMultiFormDialog } from "../modules/dialogs";
4
+
5
+
6
+ describe('createMultiFormDialog', () => {
7
+ let dialog;
8
+
9
+ beforeEach(() => {
10
+ // Create a fresh dialog element before each test
11
+ dialog = document.createElement('dialog');
12
+ dialog.innerHTML = `
13
+ <fieldset data-title="fieldset1">
14
+ <input type="text" >
15
+ </fieldset>
16
+ <fieldset data-title="fieldset2">
17
+ <input type="text" >
18
+ </fieldset>
19
+ `;
20
+ });
21
+
22
+ test('it initializes buttons and fieldsets', () => {
23
+ createMultiFormDialog(dialog);
24
+
25
+ expect(dialog.querySelectorAll('.steps').length).toEqual(1);
26
+ });
27
+
28
+ test('it validates fieldsets and updates classes', () => {
29
+ createMultiFormDialog(dialog);
30
+
31
+ // Simulate a button click to trigger validation
32
+ const button = dialog.querySelector('button[data-title="fieldset1"]');
33
+ button.click();
34
+
35
+ // Add your assertions here to check if validation and class updates are working as expected
36
+ expect(dialog.querySelectorAll('fieldset[data-title="fieldset1"].active').length).toEqual(1);
37
+ });
38
+
39
+ test('it navigates to the next fieldset', () => {
40
+ createMultiFormDialog(dialog);
41
+
42
+ // Simulate a button click to navigate to the next fieldset
43
+ const button = dialog.querySelector('button[data-title="fieldset2"]');
44
+ button.click();
45
+
46
+ // Add your assertions here to check if navigation to the next fieldset is working
47
+ expect(dialog.querySelectorAll('fieldset[data-title="fieldset2"].active').length).toEqual(1);
48
+ });
49
+
50
+ });
@@ -16,7 +16,7 @@ const listHTML = `
16
16
 
17
17
  describe('addDataAttributes', () => {
18
18
 
19
- let list = document.createElement('ul');
19
+ let list = document.createElement('div');
20
20
  list.innerHTML = listHTML;
21
21
 
22
22
  filterTheList(list, "Lucas");