@dile/ui 3.4.4 → 3.4.6

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 (185) hide show
  1. package/components/accordion/accordion-item.js +3 -1
  2. package/components/accordion/accordion.js +3 -1
  3. package/components/app-drawer/app-drawer.js +3 -1
  4. package/components/avatar/avatar.js +3 -1
  5. package/components/avatar/avatar.spec.js +25 -0
  6. package/components/box-selector/box-selector-item.js +3 -1
  7. package/components/box-selector/box-selector-item.spec.js +27 -0
  8. package/components/box-selector/box-selector.js +3 -1
  9. package/components/box-selector/box-selector.spec.js +27 -0
  10. package/components/breadcrumbs/breadcrumbs-item.js +3 -1
  11. package/components/breadcrumbs/breadcrumbs-item.spec.js +28 -0
  12. package/components/breadcrumbs/breadcrumbs.js +3 -1
  13. package/components/breadcrumbs/breadcrumbs.spec.js +33 -0
  14. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--does-not-leak-a-stale-value-when-the-form-is-submitted-again-without-a-button-click-1.png +0 -0
  15. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--includes-name-value-in-the-formdata-event-entry-list-when-clicked-1.png +0 -0
  16. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--includes-name-value-when-read-synchronously-inside-the-submit-listener-1.png +0 -0
  17. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--only-includes-the-clicked-button-among-two-submit-buttons-with-different-values-1.png +0 -0
  18. package/components/button/button-icon.js +3 -1
  19. package/components/button/button.js +3 -1
  20. package/components/button/button.spec.js +134 -0
  21. package/components/button/src/DileButton.js +8 -2
  22. package/components/card/card-slide.js +3 -1
  23. package/components/card/card-slide.spec.js +30 -0
  24. package/components/card/card.js +3 -1
  25. package/components/card/card.spec.js +26 -0
  26. package/components/checkbox/checkbox.js +3 -1
  27. package/components/checkbox/checkbox.spec.js +235 -0
  28. package/components/checkbox/src/DileCheckbox.js +27 -15
  29. package/components/chip/chip.js +3 -1
  30. package/components/chip/chip.spec.js +31 -0
  31. package/components/color-picker/color-picker.js +3 -1
  32. package/components/color-picker/color-picker.spec.js +16 -0
  33. package/components/color-picker/src/DileColorPicker.js +1 -1
  34. package/components/confirm/confirm.js +3 -1
  35. package/components/confirm/confirm.spec.js +32 -0
  36. package/components/confirm-text/confirm-text.js +3 -1
  37. package/components/confirm-text/confirm-text.spec.js +29 -0
  38. package/components/copy-markdown-url/copy-markdown-url.spec.js +27 -0
  39. package/components/copy-markdown-url/index.js +3 -1
  40. package/components/copy-text/__screenshots__/copy-text.spec.js/dile-copy-text-dispatches-dile-text-copied-with-the-content-on-copy---1.png +0 -0
  41. package/components/copy-text/copy-text.spec.js +38 -0
  42. package/components/copy-text/index.js +3 -1
  43. package/components/drop-file/drop-file.js +3 -1
  44. package/components/drop-file/drop-file.spec.js +33 -0
  45. package/components/drop-file/file-preview.js +3 -1
  46. package/components/drop-file/file-preview.spec.js +35 -0
  47. package/components/drop-file/src/DileDropFile.js +1 -1
  48. package/components/floating-feedback/floating-feedback.spec.js +30 -0
  49. package/components/floating-feedback/index.js +3 -1
  50. package/components/floating-menu/floating-menu-item.js +3 -1
  51. package/components/floating-menu/floating-menu-item.spec.js +27 -0
  52. package/components/floating-menu/floating-menu.js +3 -1
  53. package/components/floating-menu/floating-menu.spec.js +31 -0
  54. package/components/form-separator/form-separator.js +3 -1
  55. package/components/form-separator/form-separator.spec.js +28 -0
  56. package/components/hamburger/hamburger.js +3 -1
  57. package/components/hamburger/hamburger.spec.js +28 -0
  58. package/components/icon/icon.js +3 -1
  59. package/components/icon/icon.spec.js +31 -0
  60. package/components/icon-switch/icon-switch-group.js +3 -1
  61. package/components/icon-switch/icon-switch-group.spec.js +46 -0
  62. package/components/icon-switch/icon-switch.js +3 -1
  63. package/components/icon-switch/icon-switch.spec.js +35 -0
  64. package/components/info-box/info-box.js +3 -1
  65. package/components/info-box/info-box.spec.js +38 -0
  66. package/components/inline-feedback/inline-feedback.js +3 -1
  67. package/components/inline-feedback/inline-feedback.spec.js +46 -0
  68. package/components/input/__screenshots__/input-number-mask.spec.js/dile-input-number-mask-masks-digit-keydowns-following-the-given-pattern-1.png +0 -0
  69. package/components/input/input-contact.js +3 -1
  70. package/components/input/input-contact.spec.js +32 -0
  71. package/components/input/input-integer.js +3 -1
  72. package/components/input/input-integer.spec.js +31 -0
  73. package/components/input/input-message.js +3 -1
  74. package/components/input/input-message.spec.js +27 -0
  75. package/components/input/input-money.js +3 -1
  76. package/components/input/input-money.spec.js +30 -0
  77. package/components/input/input-number-mask.js +3 -1
  78. package/components/input/input-number-mask.spec.js +32 -0
  79. package/components/input/input-percentage.js +3 -1
  80. package/components/input/input-percentage.spec.js +31 -0
  81. package/components/input/input-search.js +3 -1
  82. package/components/input/input-search.spec.js +39 -0
  83. package/components/input/input.js +3 -1
  84. package/components/input/input.spec.js +47 -0
  85. package/components/input/src/DileInput.js +1 -1
  86. package/components/light-mode-switch/light-mode-switch.js +3 -1
  87. package/components/light-mode-switch/light-mode-switch.spec.js +34 -0
  88. package/components/menu-hamburger/menu-hamburger.js +3 -1
  89. package/components/menu-hamburger/menu-hamburger.spec.js +34 -0
  90. package/components/menu-overlay/menu-overlay.js +3 -1
  91. package/components/menu-overlay/menu-overlay.spec.js +44 -0
  92. package/components/message/message.js +3 -1
  93. package/components/message/message.spec.js +42 -0
  94. package/components/modal/__screenshots__/modal-help.spec.js/dile-modal-help-opens-the-inner-modal-on-open---1.png +0 -0
  95. package/components/modal/modal-help.js +3 -1
  96. package/components/modal/modal-help.spec.js +32 -0
  97. package/components/modal/modal.js +3 -1
  98. package/components/modal/modal.spec.js +46 -0
  99. package/components/nav/nav.js +3 -1
  100. package/components/nav/nav.spec.js +33 -0
  101. package/components/number-picker/number-picker-element.js +3 -1
  102. package/components/number-picker/number-picker-element.spec.js +30 -0
  103. package/components/number-picker/number-picker.js +3 -1
  104. package/components/number-picker/number-picker.spec.js +30 -0
  105. package/components/number-picker/src/DileNumberPicker.js +1 -1
  106. package/components/order-switch/order-switch.js +3 -1
  107. package/components/order-switch/order-switch.spec.js +31 -0
  108. package/components/otp-input/otp-input.js +3 -1
  109. package/components/otp-input/otp-input.spec.js +41 -0
  110. package/components/otp-input/src/DileOtpInput.js +1 -1
  111. package/components/pages/pages.js +3 -1
  112. package/components/pages/pages.spec.js +44 -0
  113. package/components/password/password.js +3 -1
  114. package/components/password/password.spec.js +35 -0
  115. package/components/progress-bar/progress-bar.js +3 -1
  116. package/components/progress-bar/progress-bar.spec.js +28 -0
  117. package/components/radio-group/radio-group.js +3 -1
  118. package/components/radio-group/radio-group.spec.js +44 -0
  119. package/components/radio-group/radio.js +3 -1
  120. package/components/radio-group/radio.spec.js +32 -0
  121. package/components/radio-group/src/DileRadioGroup.js +1 -1
  122. package/components/range/range.js +3 -1
  123. package/components/range/range.spec.js +36 -0
  124. package/components/range/src/DileRange.js +1 -1
  125. package/components/rating/rating.js +3 -1
  126. package/components/rating/rating.spec.js +34 -0
  127. package/components/rating/star.js +3 -1
  128. package/components/rating/star.spec.js +30 -0
  129. package/components/rating-scale-question/rating-scale-option.js +3 -1
  130. package/components/rating-scale-question/rating-scale-option.spec.js +32 -0
  131. package/components/rating-scale-question/rating-scale-question.js +3 -1
  132. package/components/rating-scale-question/rating-scale-question.spec.js +35 -0
  133. package/components/select/select-ajax.js +3 -1
  134. package/components/select/select-boolean.js +3 -1
  135. package/components/select/select.js +3 -1
  136. package/components/select/select.spec.js +39 -0
  137. package/components/select/src/DileSelect.js +1 -1
  138. package/components/select/src/DileSelectAjax.js +1 -1
  139. package/components/select/src/DileSelectBoolean.js +1 -1
  140. package/components/selector/selector-item.js +3 -1
  141. package/components/selector/selector-item.spec.js +40 -0
  142. package/components/selector/selector-overlay.js +3 -1
  143. package/components/selector/selector.js +3 -1
  144. package/components/selector/selector.spec.js +45 -0
  145. package/components/slide-menu/slide-menu.js +3 -1
  146. package/components/slide-menu/slide-menu.spec.js +38 -0
  147. package/components/slide-show/slide-show.js +3 -1
  148. package/components/slide-show/slide-show.spec.js +34 -0
  149. package/components/social-icon/social-icon.js +3 -1
  150. package/components/social-icon/social-icon.spec.js +26 -0
  151. package/components/spinner/spinner-bar.js +3 -1
  152. package/components/spinner/spinner-bar.spec.js +26 -0
  153. package/components/spinner/spinner-block.js +3 -1
  154. package/components/spinner/spinner-block.spec.js +21 -0
  155. package/components/spinner/spinner-horizontal.js +3 -1
  156. package/components/spinner/spinner-horizontal.spec.js +25 -0
  157. package/components/spinner/spinner-icon.js +3 -1
  158. package/components/spinner/spinner-icon.spec.js +25 -0
  159. package/components/spinner/spinner-modal.js +3 -1
  160. package/components/spinner/spinner-modal.spec.js +26 -0
  161. package/components/spinner/spinner.js +3 -1
  162. package/components/spinner/spinner.spec.js +26 -0
  163. package/components/switch/switch.js +3 -1
  164. package/components/switch/switch.spec.js +33 -0
  165. package/components/tabs/tab.js +3 -1
  166. package/components/tabs/tabs.js +3 -1
  167. package/components/tabs/tabs.spec.js +45 -0
  168. package/components/textarea/src/DileTextarea.js +1 -1
  169. package/components/textarea/textarea.js +3 -1
  170. package/components/textarea/textarea.spec.js +39 -0
  171. package/components/time-picker/src/DileTimePicker.js +1 -1
  172. package/components/time-picker/time-picker.js +3 -1
  173. package/components/time-picker/time-picker.spec.js +26 -0
  174. package/components/timer/timer.js +3 -1
  175. package/components/timer/timer.spec.js +31 -0
  176. package/components/toast/toast-item.js +3 -1
  177. package/components/toast/toast-persistent.js +3 -1
  178. package/components/toast/toast.js +3 -1
  179. package/components/tooltip/chip-tooltip.js +3 -1
  180. package/components/tooltip/chip-tooltip.spec.js +29 -0
  181. package/components/tooltip/tooltip.js +3 -1
  182. package/components/tooltip/tooltip.spec.js +32 -0
  183. package/components/web-visor/web-visor.js +3 -1
  184. package/components/web-visor/web-visor.spec.js +32 -0
  185. package/package.json +3 -3
@@ -1,3 +1,5 @@
1
1
  import { DileAccordionItem } from './src/DileAccordionItem.js';
2
2
 
3
- window.customElements.define('dile-accordion-item', DileAccordionItem);
3
+ if (!customElements.get('dile-accordion-item')) {
4
+ window.customElements.define('dile-accordion-item', DileAccordionItem);
5
+ }
@@ -1,3 +1,5 @@
1
1
  import { DileAccordion } from './src/DileAccordion.js';
2
2
 
3
- window.customElements.define('dile-accordion', DileAccordion);
3
+ if (!customElements.get('dile-accordion')) {
4
+ window.customElements.define('dile-accordion', DileAccordion);
5
+ }
@@ -1,3 +1,5 @@
1
1
  import { DileAppDrawer } from "./src/DileAppDrawer.js";
2
2
 
3
- customElements.define("dile-app-drawer", DileAppDrawer);
3
+ if (!customElements.get("dile-app-drawer")) {
4
+ customElements.define("dile-app-drawer", DileAppDrawer);
5
+ }
@@ -1,3 +1,5 @@
1
1
  import { DileAvatar } from "./src/DileAvatar.js";
2
2
 
3
- window.customElements.define("dile-avatar", DileAvatar);
3
+ if (!customElements.get("dile-avatar")) {
4
+ window.customElements.define("dile-avatar", DileAvatar);
5
+ }
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './avatar.js';
3
+
4
+ describe('dile-avatar', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderAvatar(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-avatar');
12
+ await el.updateComplete;
13
+ return el;
14
+ }
15
+
16
+ it('renders an image placeholder div when no initial is set', async () => {
17
+ const el = await renderAvatar('<dile-avatar></dile-avatar>');
18
+ expect(el.shadowRoot.querySelector('#img')).toBeTruthy();
19
+ });
20
+
21
+ it('renders the first letter of the initial property', async () => {
22
+ const el = await renderAvatar('<dile-avatar initial="Miguel"></dile-avatar>');
23
+ expect(el.shadowRoot.querySelector('.initial').textContent.trim()).toBe('M');
24
+ });
25
+ });
@@ -1,2 +1,4 @@
1
1
  import { DileBoxSelectorItem } from './src/DileBoxSelectorItem.js';
2
- customElements.define("dile-box-selector-item", DileBoxSelectorItem);
2
+ if (!customElements.get("dile-box-selector-item")) {
3
+ customElements.define("dile-box-selector-item", DileBoxSelectorItem);
4
+ }
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './box-selector-item.js';
3
+
4
+ describe('dile-box-selector-item', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderItem(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-box-selector-item');
12
+ await el.updateComplete;
13
+ return el;
14
+ }
15
+
16
+ it('renders the label property', async () => {
17
+ const el = await renderItem('<dile-box-selector-item label="Option A"></dile-box-selector-item>');
18
+ expect(el.shadowRoot.textContent.trim()).toBe('Option A');
19
+ });
20
+
21
+ it('reflects selected attribute when clicked', async () => {
22
+ const el = await renderItem('<dile-box-selector-item label="Option A"></dile-box-selector-item>');
23
+ el.select();
24
+ await el.updateComplete;
25
+ expect(el.selected).toBe(false);
26
+ });
27
+ });
@@ -1,2 +1,4 @@
1
1
  import { DileBoxSelector } from './src/DileBoxSelector.js'
2
- customElements.define("dile-box-selector", DileBoxSelector);
2
+ if (!customElements.get("dile-box-selector")) {
3
+ customElements.define("dile-box-selector", DileBoxSelector);
4
+ }
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './box-selector.js';
3
+ import './box-selector-item.js';
4
+
5
+ describe('dile-box-selector', () => {
6
+ afterEach(() => {
7
+ document.body.innerHTML = '';
8
+ });
9
+
10
+ async function renderSelector(html) {
11
+ document.body.innerHTML = html;
12
+ const el = document.body.querySelector('dile-box-selector');
13
+ await el.updateComplete;
14
+ return el;
15
+ }
16
+
17
+ it('renders the slotted items', async () => {
18
+ const el = await renderSelector(`
19
+ <dile-box-selector>
20
+ <dile-box-selector-item label="One"></dile-box-selector-item>
21
+ <dile-box-selector-item label="Two"></dile-box-selector-item>
22
+ </dile-box-selector>
23
+ `);
24
+ expect(el.shadowRoot.querySelector('slot')).toBeTruthy();
25
+ expect(el.querySelectorAll('dile-box-selector-item').length).toBe(2);
26
+ });
27
+ });
@@ -1,3 +1,5 @@
1
1
  import { DileBreadcrumbsItem } from "./src/DileBreadcrumbsItem.js";
2
2
 
3
- window.customElements.define("dile-breadcrumbs-item", DileBreadcrumbsItem);
3
+ if (!customElements.get("dile-breadcrumbs-item")) {
4
+ window.customElements.define("dile-breadcrumbs-item", DileBreadcrumbsItem);
5
+ }
@@ -0,0 +1,28 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './breadcrumbs-item.js';
3
+
4
+ describe('dile-breadcrumbs-item', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderItem(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-breadcrumbs-item');
12
+ await el.updateComplete;
13
+ return el;
14
+ }
15
+
16
+ it('renders an anchor when href is set', async () => {
17
+ const el = await renderItem('<dile-breadcrumbs-item href="/home">Home</dile-breadcrumbs-item>');
18
+ const anchor = el.shadowRoot.querySelector('a');
19
+ expect(anchor).toBeTruthy();
20
+ expect(anchor.getAttribute('href')).toBe('/home');
21
+ });
22
+
23
+ it('renders a span when href is not set', async () => {
24
+ const el = await renderItem('<dile-breadcrumbs-item>Current</dile-breadcrumbs-item>');
25
+ expect(el.shadowRoot.querySelector('span')).toBeTruthy();
26
+ expect(el.shadowRoot.querySelector('a')).toBeNull();
27
+ });
28
+ });
@@ -1,4 +1,6 @@
1
1
  import './breadcrumbs-item.js';
2
2
  import { DileBreadcrumbs } from "./src/DileBreadcrumbs.js";
3
3
 
4
- window.customElements.define("dile-breadcrumbs", DileBreadcrumbs);
4
+ if (!customElements.get("dile-breadcrumbs")) {
5
+ window.customElements.define("dile-breadcrumbs", DileBreadcrumbs);
6
+ }
@@ -0,0 +1,33 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './breadcrumbs.js';
3
+
4
+ describe('dile-breadcrumbs', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderBreadcrumbs(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-breadcrumbs');
12
+ await el.updateComplete;
13
+ return el;
14
+ }
15
+
16
+ it('renders slotted breadcrumb items', async () => {
17
+ const el = await renderBreadcrumbs(`
18
+ <dile-breadcrumbs>
19
+ <dile-breadcrumbs-item href="/">Home</dile-breadcrumbs-item>
20
+ <dile-breadcrumbs-item>Current</dile-breadcrumbs-item>
21
+ </dile-breadcrumbs>
22
+ `);
23
+ expect(el.querySelectorAll('dile-breadcrumbs-item').length).toBe(2);
24
+ });
25
+
26
+ it('renders items from the items property', async () => {
27
+ const el = await renderBreadcrumbs('<dile-breadcrumbs></dile-breadcrumbs>');
28
+ el.items = [{ text: 'Home', href: '/' }, { text: 'Page' }];
29
+ await el.updateComplete;
30
+
31
+ expect(el.shadowRoot.querySelectorAll('dile-breadcrumbs-item').length).toBe(2);
32
+ });
33
+ });
@@ -1,3 +1,5 @@
1
1
  import { DileButtonIcon } from "./src/DileButtonIcon.js";
2
2
 
3
- window.customElements.define("dile-button-icon", DileButtonIcon);
3
+ if (!customElements.get("dile-button-icon")) {
4
+ window.customElements.define("dile-button-icon", DileButtonIcon);
5
+ }
@@ -1,3 +1,5 @@
1
1
  import { DileButton } from "./src/DileButton.js";
2
2
 
3
- window.customElements.define("dile-button", DileButton);
3
+ if (!customElements.get("dile-button")) {
4
+ window.customElements.define("dile-button", DileButton);
5
+ }
@@ -45,4 +45,138 @@ describe('dile-button', () => {
45
45
 
46
46
  expect(el.shadowRoot.querySelector('dile-spinner-icon')).toBeTruthy();
47
47
  });
48
+
49
+ describe('Native form submission', () => {
50
+ it('includes name/value in the FormData when clicked', async () => {
51
+ document.body.innerHTML = `
52
+ <form id="testForm">
53
+ <dile-button type="submit" name="action" value="save">Save</dile-button>
54
+ </form>
55
+ `;
56
+ const form = document.getElementById('testForm');
57
+ const el = document.body.querySelector('dile-button');
58
+ await el.updateComplete;
59
+
60
+ let capturedFormData = null;
61
+ form.addEventListener('submit', (e) => {
62
+ e.preventDefault();
63
+ capturedFormData = new FormData(form);
64
+ });
65
+
66
+ el.shadowRoot.querySelector('button').click();
67
+
68
+ expect(capturedFormData.get('action')).toBe('save');
69
+ });
70
+
71
+ it('does not submit a name/value pair when the button has no value', async () => {
72
+ document.body.innerHTML = `
73
+ <form id="testForm">
74
+ <dile-button type="submit" name="action">Save</dile-button>
75
+ </form>
76
+ `;
77
+ const form = document.getElementById('testForm');
78
+ const el = document.body.querySelector('dile-button');
79
+ await el.updateComplete;
80
+
81
+ let capturedFormData = null;
82
+ form.addEventListener('submit', (e) => {
83
+ e.preventDefault();
84
+ capturedFormData = new FormData(form);
85
+ });
86
+
87
+ el.shadowRoot.querySelector('button').click();
88
+
89
+ expect(capturedFormData.has('action')).toBe(false);
90
+ });
91
+
92
+ it('participates in FormData when name is set as a JS property', async () => {
93
+ document.body.innerHTML = `
94
+ <form id="testForm">
95
+ <dile-button type="submit" value="save">Save</dile-button>
96
+ </form>
97
+ `;
98
+ const form = document.getElementById('testForm');
99
+ const el = document.body.querySelector('dile-button');
100
+ el.name = 'action';
101
+ await el.updateComplete;
102
+
103
+ expect(el.getAttribute('name')).toBe('action');
104
+
105
+ let capturedFormData = null;
106
+ form.addEventListener('submit', (e) => {
107
+ e.preventDefault();
108
+ capturedFormData = new FormData(form);
109
+ });
110
+
111
+ el.shadowRoot.querySelector('button').click();
112
+
113
+ expect(capturedFormData.get('action')).toBe('save');
114
+ });
115
+
116
+ it('only includes the clicked button among two submit buttons with different values', async () => {
117
+ document.body.innerHTML = `
118
+ <form id="testForm">
119
+ <dile-button type="submit" name="action" value="save">Save</dile-button>
120
+ <dile-button type="submit" name="action" value="delete">Delete</dile-button>
121
+ </form>
122
+ `;
123
+ const form = document.getElementById('testForm');
124
+ const buttons = document.body.querySelectorAll('dile-button');
125
+ for (const b of buttons) await b.updateComplete;
126
+
127
+ let capturedFormData = null;
128
+ form.addEventListener('submit', (e) => {
129
+ e.preventDefault();
130
+ capturedFormData = new FormData(form);
131
+ });
132
+
133
+ buttons[1].shadowRoot.querySelector('button').click();
134
+
135
+ expect(capturedFormData.getAll('action')).toEqual(['delete']);
136
+ });
137
+
138
+ it('does not leak a stale value when the form is submitted again without a button click', async () => {
139
+ document.body.innerHTML = `
140
+ <form id="testForm">
141
+ <input name="text" value="hello" />
142
+ <dile-button type="submit" name="action" value="save">Save</dile-button>
143
+ </form>
144
+ `;
145
+ const form = document.getElementById('testForm');
146
+ const el = document.body.querySelector('dile-button');
147
+ await el.updateComplete;
148
+
149
+ let capturedFormData = null;
150
+ form.addEventListener('submit', (e) => {
151
+ e.preventDefault();
152
+ capturedFormData = new FormData(form);
153
+ });
154
+
155
+ // First submit via the button click - action=save should be present.
156
+ el.shadowRoot.querySelector('button').click();
157
+ expect(capturedFormData.get('action')).toBe('save');
158
+
159
+ // Second submit programmatically, with no submitter clicked - action should NOT leak.
160
+ form.requestSubmit();
161
+ expect(capturedFormData.get('action')).toBeNull();
162
+ });
163
+
164
+ it('does not submit when the button is disabled', async () => {
165
+ document.body.innerHTML = `
166
+ <form id="testForm">
167
+ <dile-button type="submit" name="action" value="save" disabled>Save</dile-button>
168
+ </form>
169
+ `;
170
+ const form = document.getElementById('testForm');
171
+ const el = document.body.querySelector('dile-button');
172
+ await el.updateComplete;
173
+
174
+ let submitted = false;
175
+ form.addEventListener('submit', (e) => { e.preventDefault(); submitted = true; });
176
+
177
+ el.shadowRoot.querySelector('button').click();
178
+
179
+ expect(submitted).toBe(false);
180
+ });
181
+ });
48
182
  });
@@ -9,8 +9,9 @@ export class DileButton extends LitElement {
9
9
  return {
10
10
  disabled: { type: Boolean },
11
11
  loading: { type: Boolean, reflect: true },
12
- name: { type: String },
12
+ name: { type: String, reflect: true },
13
13
  type: { type: String },
14
+ value: { type: String },
14
15
  };
15
16
  }
16
17
 
@@ -134,7 +135,12 @@ export class DileButton extends LitElement {
134
135
  }
135
136
 
136
137
  if (this.type === "submit") {
137
- this._internals.form?.requestSubmit();
138
+ const form = this._internals.form;
139
+ if (form) {
140
+ this._internals.setFormValue(this.value ?? null);
141
+ form.requestSubmit();
142
+ this._internals.setFormValue(null);
143
+ }
138
144
  } else if (this.type === "reset") {
139
145
  this._internals.form?.reset();
140
146
  }
@@ -1,3 +1,5 @@
1
1
  import { DileCardSlide } from "./src/DileCardSlide.js";
2
2
 
3
- window.customElements.define("dile-card-slide", DileCardSlide);
3
+ if (!customElements.get("dile-card-slide")) {
4
+ window.customElements.define("dile-card-slide", DileCardSlide);
5
+ }
@@ -0,0 +1,30 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './card-slide.js';
3
+
4
+ describe('dile-card-slide', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderCardSlide(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-card-slide');
12
+ await el.updateComplete;
13
+ return el;
14
+ }
15
+
16
+ it('renders the title as a toggle button', async () => {
17
+ const el = await renderCardSlide('<dile-card-slide title="My title">Content</dile-card-slide>');
18
+ const button = el.shadowRoot.querySelector('.card-title button');
19
+ expect(button).toBeTruthy();
20
+ expect(el.opened).toBe(false);
21
+ });
22
+
23
+ it('toggles opened state when clicked', async () => {
24
+ const el = await renderCardSlide('<dile-card-slide title="My title">Content</dile-card-slide>');
25
+ el.shadowRoot.querySelector('.card-title button').click();
26
+ await el.updateComplete;
27
+
28
+ expect(el.opened).toBe(true);
29
+ });
30
+ });
@@ -1,3 +1,5 @@
1
1
  import { DileCard } from "./src/DileCard.js";
2
2
 
3
- window.customElements.define("dile-card", DileCard);
3
+ if (!customElements.get("dile-card")) {
4
+ window.customElements.define("dile-card", DileCard);
5
+ }
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './card.js';
3
+
4
+ describe('dile-card', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderCard(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-card');
12
+ await el.updateComplete;
13
+ return el;
14
+ }
15
+
16
+ it('renders the title and slotted content', async () => {
17
+ const el = await renderCard('<dile-card title="My title">Content</dile-card>');
18
+ expect(el.shadowRoot.querySelector('.card-title').textContent.trim()).toBe('My title');
19
+ expect(el.textContent.trim()).toBe('Content');
20
+ });
21
+
22
+ it('does not render a title element when title is not set', async () => {
23
+ const el = await renderCard('<dile-card>Content</dile-card>');
24
+ expect(el.shadowRoot.querySelector('.card-title')).toBeNull();
25
+ });
26
+ });
@@ -1,3 +1,5 @@
1
1
  import { DileCheckbox } from "./src/DileCheckbox.js";
2
2
 
3
- window.customElements.define("dile-checkbox", DileCheckbox);
3
+ if (!customElements.get("dile-checkbox")) {
4
+ window.customElements.define("dile-checkbox", DileCheckbox);
5
+ }