@dile/ui 3.18.0 → 3.18.2

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 (115) hide show
  1. package/components/floating-menu/src/DileFloatingMenu.js +195 -61
  2. package/components/floating-menu/src/DileFloatingMenuItem.js +12 -5
  3. package/components/toast/src/DileToast.js +2 -1
  4. package/components/toast/src/DileToastPersistent.js +1 -1
  5. package/package.json +4 -4
  6. package/components/accordion/accordion.spec.js +0 -253
  7. package/components/app-drawer/app-drawer.spec.js +0 -262
  8. package/components/avatar/avatar.spec.js +0 -25
  9. package/components/bottom-nav/__screenshots__/bottom-nav.spec.js/dile-bottom-nav-places-the-sheet-and-the-overlay-above-the-bar-1.png +0 -0
  10. package/components/bottom-nav/bottom-nav.spec.js +0 -340
  11. package/components/box-selector/box-selector-item.spec.js +0 -27
  12. package/components/box-selector/box-selector.spec.js +0 -27
  13. package/components/breadcrumbs/breadcrumbs-item.spec.js +0 -28
  14. package/components/breadcrumbs/breadcrumbs.spec.js +0 -33
  15. package/components/button/button.spec.js +0 -234
  16. package/components/card/card-slide.spec.js +0 -30
  17. package/components/card/card.spec.js +0 -26
  18. package/components/checkbox/checkbox.spec.js +0 -235
  19. package/components/chip/chip.spec.js +0 -31
  20. package/components/color-picker/color-picker.spec.js +0 -133
  21. package/components/confirm/confirm.spec.js +0 -57
  22. package/components/confirm-text/confirm-text.spec.js +0 -29
  23. package/components/copy-markdown-url/copy-markdown-url.spec.js +0 -27
  24. package/components/copy-text/copy-text.spec.js +0 -38
  25. package/components/data-grid/__screenshots__/data-grid.spec.js/dile-data-grid-dispatches-item-checkbox-changed-and-dile-data-grid-item-selected-when-a-checkbox-is-toggled-1.png +0 -0
  26. package/components/data-grid/data-grid.spec.js +0 -421
  27. package/components/drop-file/drop-file.spec.js +0 -214
  28. package/components/drop-file/file-preview.spec.js +0 -35
  29. package/components/floating-feedback/floating-feedback.spec.js +0 -30
  30. package/components/floating-menu/floating-menu-item.spec.js +0 -27
  31. package/components/floating-menu/floating-menu.spec.js +0 -31
  32. package/components/form-separator/form-separator.spec.js +0 -28
  33. package/components/hamburger/hamburger.spec.js +0 -28
  34. package/components/icon/icon.spec.js +0 -31
  35. package/components/icon-switch/icon-switch-group.spec.js +0 -46
  36. package/components/icon-switch/icon-switch.spec.js +0 -35
  37. package/components/info-box/info-box.spec.js +0 -38
  38. package/components/inline-feedback/inline-feedback.spec.js +0 -46
  39. package/components/input/input-contact.spec.js +0 -32
  40. package/components/input/input-icon.spec.js +0 -166
  41. package/components/input/input-integer.spec.js +0 -31
  42. package/components/input/input-mask.spec.js +0 -314
  43. package/components/input/input-message.spec.js +0 -27
  44. package/components/input/input-money.spec.js +0 -30
  45. package/components/input/input-number-mask.spec.js +0 -48
  46. package/components/input/input-number.spec.js +0 -109
  47. package/components/input/input-percentage.spec.js +0 -31
  48. package/components/input/input-search.spec.js +0 -91
  49. package/components/input/input.spec.js +0 -47
  50. package/components/light-mode-switch/light-mode-switch.spec.js +0 -34
  51. package/components/menu-hamburger/menu-hamburger.spec.js +0 -34
  52. package/components/menu-overlay/menu-overlay.spec.js +0 -44
  53. package/components/message/message.spec.js +0 -42
  54. package/components/modal/__screenshots__/modal.spec.js/dile-modal-closes-only-the-topmost-modal-when-Esc-is-pressed-1.png +0 -0
  55. package/components/modal/__screenshots__/modal.spec.js/dile-modal-dispatches-dile-modal-closed-and-animates-when-the-opened-property-is-set-to-false-1.png +0 -0
  56. package/components/modal/__screenshots__/modal.spec.js/dile-modal-does-not-close-a-blocking-modal-when-Esc-is-pressed-1.png +0 -0
  57. package/components/modal/__screenshots__/modal.spec.js/dile-modal-does-not-close-the-modal-below-when-the-topmost-modal-is-blocking-1.png +0 -0
  58. package/components/modal/__screenshots__/modal.spec.js/dile-modal-ignores-transitionend-events-coming-from-inner-elements-1.png +0 -0
  59. package/components/modal/__screenshots__/modal.spec.js/dile-modal-keeps-the-focus-on-content-focused-by-the-user-when-opening-1.png +0 -0
  60. package/components/modal/__screenshots__/modal.spec.js/dile-modal-opens-and-closes-through-the-opened-attribute-1.png +0 -0
  61. package/components/modal/__screenshots__/modal.spec.js/dile-modal-reflects-the-opened-property-to-the-attribute-1.png +0 -0
  62. package/components/modal/__screenshots__/modal.spec.js/dile-modal-releases-the-background-layer-after-the-close-animation-even-without-transitionend-1.png +0 -0
  63. package/components/modal/__screenshots__/modal.spec.js/dile-modal-releases-the-background-layer-immediately-when-the-animation-duration-is-0s-1.png +0 -0
  64. package/components/modal/modal-help.spec.js +0 -32
  65. package/components/modal/modal.spec.js +0 -250
  66. package/components/nav/nav.spec.js +0 -33
  67. package/components/number-picker/number-picker-element.spec.js +0 -30
  68. package/components/number-picker/number-picker.spec.js +0 -30
  69. package/components/order-switch/__screenshots__/order-switch.spec.js/dile-order-switch-clicking-the-label-re-announces-the-current-value-without-toggling-when-not-selected-1.png +0 -0
  70. package/components/order-switch/__screenshots__/order-switch.spec.js/dile-order-switch-clicking-the-label-toggles-the-order-when-selected-1.png +0 -0
  71. package/components/order-switch/__screenshots__/order-switch.spec.js/dile-order-switch-defaults-to-ascending-order-1.png +0 -0
  72. package/components/order-switch/order-switch.spec.js +0 -57
  73. package/components/otp-input/otp-input.spec.js +0 -41
  74. package/components/pages/pages.spec.js +0 -44
  75. package/components/password/password.spec.js +0 -35
  76. package/components/progress-bar/progress-bar.spec.js +0 -28
  77. package/components/radio-group/__screenshots__/radio-group-dialog.spec.js/dile-radio-group-dialog-renders-dialog-items-with-labels-and-values-1.png +0 -0
  78. package/components/radio-group/__screenshots__/radio-group-dialog.spec.js/dile-radio-group-dialog-renders-items-with-optional-description-1.png +0 -0
  79. package/components/radio-group/__screenshots__/radio-group-dialog.spec.js/dile-radio-group-dialog-updates-value-when-an-option-is-selected-1.png +0 -0
  80. package/components/radio-group/radio-group-dialog.spec.js +0 -126
  81. package/components/radio-group/radio-group.spec.js +0 -128
  82. package/components/radio-group/radio.spec.js +0 -32
  83. package/components/range/range.spec.js +0 -36
  84. package/components/rating/rating.spec.js +0 -34
  85. package/components/rating/star.spec.js +0 -30
  86. package/components/rating-scale-question/rating-scale-option.spec.js +0 -32
  87. package/components/rating-scale-question/rating-scale-question.spec.js +0 -35
  88. package/components/select/__screenshots__/select-search.spec.js/dile-select-search-Value-API-uses-the-label-as-accessible-name-of-the-selected-field-1.png +0 -0
  89. package/components/select/select-ajax-overlay.spec.js +0 -469
  90. package/components/select/select-overlay.spec.js +0 -612
  91. package/components/select/select-search.spec.js +0 -318
  92. package/components/select/select.spec.js +0 -574
  93. package/components/selector/selector-item.spec.js +0 -40
  94. package/components/selector/selector-overlay.spec.js +0 -139
  95. package/components/selector/selector.spec.js +0 -45
  96. package/components/slide-menu/slide-menu.spec.js +0 -38
  97. package/components/slide-show/slide-show.spec.js +0 -34
  98. package/components/social-icon/social-icon.spec.js +0 -26
  99. package/components/spinner/spinner-bar.spec.js +0 -26
  100. package/components/spinner/spinner-block.spec.js +0 -21
  101. package/components/spinner/spinner-horizontal.spec.js +0 -25
  102. package/components/spinner/spinner-icon.spec.js +0 -25
  103. package/components/spinner/spinner-modal.spec.js +0 -26
  104. package/components/spinner/spinner.spec.js +0 -26
  105. package/components/steps/steps.spec.js +0 -84
  106. package/components/switch/switch.spec.js +0 -33
  107. package/components/tabs/tabs.spec.js +0 -85
  108. package/components/textarea/textarea.spec.js +0 -39
  109. package/components/time-picker/time-picker.spec.js +0 -26
  110. package/components/timer/timer.spec.js +0 -31
  111. package/components/toast/toast.spec.js +0 -86
  112. package/components/tooltip/chip-tooltip.spec.js +0 -29
  113. package/components/tooltip/tooltip.spec.js +0 -32
  114. package/components/web-visor/web-visor.spec.js +0 -32
  115. package/mixins/form/src/DileForm.spec.js +0 -29
@@ -1,253 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import './accordion.js';
3
- import './accordion-item.js';
4
-
5
- describe('DileAccordion - Accessibility', () => {
6
- afterEach(() => {
7
- document.body.innerHTML = '';
8
- });
9
-
10
- async function renderAccordion(html) {
11
- document.body.innerHTML = html;
12
- const el = document.body.querySelector('dile-accordion-item') || document.body.querySelector('dile-accordion');
13
- await el.updateComplete;
14
- return el;
15
- }
16
-
17
- async function renderAccordionGroup(html) {
18
- document.body.innerHTML = html;
19
- const el = document.body.querySelector('dile-accordion');
20
- const items = el.querySelectorAll('dile-accordion-item');
21
- await el.updateComplete;
22
- for (const item of items) {
23
- await item.updateComplete;
24
- }
25
- return { accordion: el, items };
26
- }
27
-
28
- describe('Semantic Structure & ARIA', () => {
29
- it('should have region role with aria-label on container', async () => {
30
- const { accordion } = await renderAccordionGroup(`
31
- <dile-accordion>
32
- <dile-accordion-item title="Item 1">
33
- <div slot="accordion-item-content">Content 1</div>
34
- </dile-accordion-item>
35
- </dile-accordion>
36
- `);
37
-
38
- const region = accordion.shadowRoot.querySelector('[role="region"]');
39
-
40
- expect(region).toBeTruthy();
41
- expect(region.getAttribute('aria-label')).toBe('Accordion');
42
- });
43
-
44
- it('button should have aria-expanded attribute', async () => {
45
- const el = await renderAccordion(`
46
- <dile-accordion-item title="Expandable">
47
- <div slot="accordion-item-content">Content</div>
48
- </dile-accordion-item>
49
- `);
50
-
51
- const button = el.shadowRoot.querySelector('button');
52
- expect(button.getAttribute('aria-expanded')).toBe('false');
53
-
54
- button.click();
55
- await el.updateComplete;
56
-
57
- expect(button.getAttribute('aria-expanded')).toBe('true');
58
- });
59
-
60
- it('button should have aria-controls pointing to content', async () => {
61
- const el = await renderAccordion(`
62
- <dile-accordion-item title="Item">
63
- <div slot="accordion-item-content">Content</div>
64
- </dile-accordion-item>
65
- `);
66
-
67
- const button = el.shadowRoot.querySelector('button');
68
- const contentContainer = el.shadowRoot.querySelector('.contentContainer');
69
-
70
- const controlsId = button.getAttribute('aria-controls');
71
- expect(controlsId).toBeTruthy();
72
- expect(contentContainer.id).toBe(controlsId);
73
- });
74
-
75
- it('content region should have aria-labelledby pointing to button', async () => {
76
- const el = await renderAccordion(`
77
- <dile-accordion-item title="Section Title">
78
- <div slot="accordion-item-content">Content</div>
79
- </dile-accordion-item>
80
- `);
81
-
82
- const button = el.shadowRoot.querySelector('button');
83
- const contentRegion = el.shadowRoot.querySelector('[role="region"]');
84
-
85
- expect(contentRegion.getAttribute('aria-labelledby')).toBe(button.id);
86
- });
87
-
88
- it('icon should be hidden from screen readers with aria-hidden', async () => {
89
- const el = await renderAccordion(`
90
- <dile-accordion-item title="Item">
91
- <div slot="accordion-item-content">Content</div>
92
- </dile-accordion-item>
93
- `);
94
-
95
- const icon = el.shadowRoot.querySelector('span.icon');
96
- expect(icon.getAttribute('aria-hidden')).toBe('true');
97
- });
98
- });
99
-
100
- describe('Keyboard Navigation', () => {
101
- it('button should be keyboard focusable', async () => {
102
- const el = await renderAccordion(`
103
- <dile-accordion-item title="Item">
104
- <div slot="accordion-item-content">Content</div>
105
- </dile-accordion-item>
106
- `);
107
-
108
- const button = el.shadowRoot.querySelector('button');
109
- button.focus();
110
-
111
- expect(el.shadowRoot.activeElement).toBe(button);
112
- });
113
-
114
- it('button should toggle on click', async () => {
115
- const el = await renderAccordion(`
116
- <dile-accordion-item title="Item">
117
- <div slot="accordion-item-content">Content</div>
118
- </dile-accordion-item>
119
- `);
120
-
121
- const button = el.shadowRoot.querySelector('button');
122
-
123
- expect(el.opened).toBe(false);
124
- button.click();
125
- await el.updateComplete;
126
-
127
- expect(el.opened).toBe(true);
128
- });
129
- });
130
-
131
- describe('Focus Management', () => {
132
- it('should have visible focus outline (focus-visible)', async () => {
133
- const el = await renderAccordion(`
134
- <dile-accordion-item title="Item">
135
- <div slot="accordion-item-content">Content</div>
136
- </dile-accordion-item>
137
- `);
138
-
139
- const button = el.shadowRoot.querySelector('button');
140
-
141
- // Verify focus-visible CSS exists
142
- const styles = el.shadowRoot.adoptedStyleSheets[0];
143
- let hasFocusVisible = false;
144
- for (const rule of styles.cssRules) {
145
- if (rule.selectorText && rule.selectorText.includes('focus-visible')) {
146
- hasFocusVisible = true;
147
- break;
148
- }
149
- }
150
-
151
- expect(hasFocusVisible).toBe(true);
152
- });
153
- });
154
-
155
- describe('Live Region Announcements', () => {
156
- it('should dispatch live region announcement on expand', async () => {
157
- const el = await renderAccordion(`
158
- <dile-accordion-item title="Settings">
159
- <div slot="accordion-item-content">Expanded content</div>
160
- </dile-accordion-item>
161
- `);
162
-
163
- const button = el.shadowRoot.querySelector('button');
164
- button.click();
165
- await el.updateComplete;
166
- await new Promise(resolve => setTimeout(resolve, 100));
167
-
168
- // Verify that a live region element exists with status role
169
- const liveRegions = el.querySelectorAll('[role="status"][aria-live]');
170
- expect(liveRegions.length).toBeGreaterThanOrEqual(0);
171
- });
172
- });
173
-
174
- describe('Content Visibility & Structure', () => {
175
- it('content should be hidden when closed', async () => {
176
- const el = await renderAccordion(`
177
- <dile-accordion-item title="Item">
178
- <div slot="accordion-item-content">Hidden Content</div>
179
- </dile-accordion-item>
180
- `);
181
-
182
- expect(el.opened).toBe(false);
183
- const button = el.shadowRoot.querySelector('button');
184
- expect(button.className).not.toContain('opened');
185
- });
186
-
187
- it('content should be visible when opened', async () => {
188
- const el = await renderAccordion(`
189
- <dile-accordion-item title="Item" opened>
190
- <div slot="accordion-item-content">Visible Content</div>
191
- </dile-accordion-item>
192
- `);
193
-
194
- // Give time for animation to trigger
195
- await new Promise(resolve => setTimeout(resolve, 150));
196
-
197
- expect(el.opened).toBe(true);
198
- const button = el.shadowRoot.querySelector('button');
199
- expect(button.className).toContain('opened');
200
- });
201
- });
202
-
203
- describe('Multiple Items', () => {
204
- it('only one item should be open at a time (mutually exclusive)', async () => {
205
- const { accordion, items } = await renderAccordionGroup(`
206
- <dile-accordion>
207
- <dile-accordion-item title="Item 1">
208
- <div slot="accordion-item-content">Content 1</div>
209
- </dile-accordion-item>
210
- <dile-accordion-item title="Item 2">
211
- <div slot="accordion-item-content">Content 2</div>
212
- </dile-accordion-item>
213
- </dile-accordion>
214
- `);
215
-
216
- const button1 = items[0].shadowRoot.querySelector('button');
217
- const button2 = items[1].shadowRoot.querySelector('button');
218
-
219
- button1.click();
220
- await items[0].updateComplete;
221
- expect(items[0].opened).toBe(true);
222
-
223
- button2.click();
224
- await items[1].updateComplete;
225
- await items[0].updateComplete;
226
-
227
- expect(items[0].opened).toBe(false);
228
- expect(items[1].opened).toBe(true);
229
- });
230
- });
231
-
232
- describe('Unique IDs', () => {
233
- it('each accordion item should have unique IDs', async () => {
234
- const { items } = await renderAccordionGroup(`
235
- <dile-accordion>
236
- <dile-accordion-item title="Item 1">
237
- <div slot="accordion-item-content">Content 1</div>
238
- </dile-accordion-item>
239
- <dile-accordion-item title="Item 2">
240
- <div slot="accordion-item-content">Content 2</div>
241
- </dile-accordion-item>
242
- </dile-accordion>
243
- `);
244
-
245
- const button1Id = items[0].shadowRoot.querySelector('button').id;
246
- const button2Id = items[1].shadowRoot.querySelector('button').id;
247
-
248
- expect(button1Id).toBeTruthy();
249
- expect(button2Id).toBeTruthy();
250
- expect(button1Id).not.toBe(button2Id);
251
- });
252
- });
253
- });
@@ -1,262 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import './app-drawer.js';
3
- import { DILE_APP_DRAWER_EVENT_CLOSED, DILE_APP_DRAWER_EVENT_CLOSE_OUTSIDE } from './index.js';
4
-
5
- describe('dile-app-drawer', () => {
6
- afterEach(() => {
7
- document.body.innerHTML = '';
8
- });
9
-
10
- function wait(ms) {
11
- return new Promise((resolve) => setTimeout(resolve, ms));
12
- }
13
-
14
- async function renderDrawer(html) {
15
- document.body.innerHTML = html;
16
- const el = document.body.querySelector('dile-app-drawer');
17
- await el.updateComplete;
18
- return el;
19
- }
20
-
21
- describe('Default state', () => {
22
- it('defaults to direction "top" and closed', async () => {
23
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
24
-
25
- expect(el.direction).toBe('top');
26
- expect(el.opened).toBe(false);
27
- });
28
-
29
- it('reflects the opened property to the opened attribute', async () => {
30
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
31
-
32
- el.open();
33
- await el.updateComplete;
34
-
35
- expect(el.hasAttribute('opened')).toBe(true);
36
- });
37
- });
38
-
39
- describe('Public API', () => {
40
- it('open() sets opened to true', async () => {
41
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
42
-
43
- el.open();
44
- await el.updateComplete;
45
-
46
- expect(el.opened).toBe(true);
47
- });
48
-
49
- it('close() sets opened to false and dispatches dile-app-drawer-closed', async () => {
50
- const el = await renderDrawer('<dile-app-drawer opened></dile-app-drawer>');
51
- let fired = false;
52
- el.addEventListener(DILE_APP_DRAWER_EVENT_CLOSED, () => { fired = true; });
53
-
54
- el.close();
55
- await el.updateComplete;
56
-
57
- expect(el.opened).toBe(false);
58
- expect(fired).toBe(true);
59
- });
60
-
61
- it('toggle() flips the opened state', async () => {
62
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
63
-
64
- el.toggle();
65
- await el.updateComplete;
66
- expect(el.opened).toBe(true);
67
-
68
- el.toggle();
69
- await el.updateComplete;
70
- expect(el.opened).toBe(false);
71
- });
72
- });
73
-
74
- describe('Modal overlay', () => {
75
- it('renders a modal overlay by default', async () => {
76
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
77
-
78
- expect(el.shadowRoot.querySelector('.modal')).toBeTruthy();
79
- });
80
-
81
- it('does not render a modal overlay when no-modal is set', async () => {
82
- const el = await renderDrawer('<dile-app-drawer no-modal></dile-app-drawer>');
83
-
84
- expect(el.shadowRoot.querySelector('.modal')).toBeNull();
85
- });
86
-
87
- it('clicking the overlay closes the drawer and dispatches closed + click-outside events', async () => {
88
- const el = await renderDrawer('<dile-app-drawer opened></dile-app-drawer>');
89
- let closedFired = false;
90
- let outsideFired = false;
91
- el.addEventListener(DILE_APP_DRAWER_EVENT_CLOSED, () => { closedFired = true; });
92
- el.addEventListener(DILE_APP_DRAWER_EVENT_CLOSE_OUTSIDE, () => { outsideFired = true; });
93
-
94
- el.shadowRoot.querySelector('.modal').click();
95
- await el.updateComplete;
96
-
97
- expect(el.opened).toBe(false);
98
- expect(closedFired).toBe(true);
99
- expect(outsideFired).toBe(true);
100
- });
101
-
102
- it('clicking the overlay while already closed does not dispatch events', async () => {
103
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
104
- let outsideFired = false;
105
- el.addEventListener(DILE_APP_DRAWER_EVENT_CLOSE_OUTSIDE, () => { outsideFired = true; });
106
-
107
- el.shadowRoot.querySelector('.modal').click();
108
- await el.updateComplete;
109
-
110
- expect(outsideFired).toBe(false);
111
- });
112
-
113
- it('stops click events on the menu content from bubbling past the drawer', async () => {
114
- const el = await renderDrawer('<div id="wrapper"><dile-app-drawer opened></dile-app-drawer></div>');
115
- const wrapper = document.querySelector('#wrapper');
116
- let wrapperClicked = false;
117
- wrapper.addEventListener('click', () => { wrapperClicked = true; });
118
-
119
- el.shadowRoot.querySelector('.menu').click();
120
-
121
- expect(wrapperClicked).toBe(false);
122
- expect(el.opened).toBe(true);
123
- });
124
- });
125
-
126
- describe('Dialog accessibility', () => {
127
- it('renders the menu as a labeled, described dialog', async () => {
128
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
129
- const menu = el.shadowRoot.querySelector('.menu');
130
-
131
- expect(menu.getAttribute('role')).toBe('dialog');
132
- expect(menu.getAttribute('aria-label')).toBe('Application menu');
133
- expect(menu.getAttribute('aria-description')).toContain('Escape');
134
- });
135
-
136
- it('sets aria-modal to reflect the opened state', async () => {
137
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
138
- const menu = el.shadowRoot.querySelector('.menu');
139
-
140
- expect(menu.getAttribute('aria-modal')).toBe('false');
141
-
142
- el.open();
143
- await el.updateComplete;
144
-
145
- expect(menu.getAttribute('aria-modal')).toBe('true');
146
- });
147
-
148
- it('marks the menu (and overlay) inert while closed, interactive while opened', async () => {
149
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
150
- const menu = el.shadowRoot.querySelector('.menu');
151
- const modal = el.shadowRoot.querySelector('.modal');
152
-
153
- expect(menu.hasAttribute('inert')).toBe(true);
154
- expect(modal.hasAttribute('inert')).toBe(true);
155
-
156
- el.open();
157
- await el.updateComplete;
158
-
159
- expect(menu.hasAttribute('inert')).toBe(false);
160
- expect(modal.hasAttribute('inert')).toBe(false);
161
- });
162
- });
163
-
164
- describe('Keyboard - Escape', () => {
165
- it('closes the drawer when Escape is pressed while opened', async () => {
166
- const el = await renderDrawer('<dile-app-drawer opened></dile-app-drawer>');
167
-
168
- window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
169
- await el.updateComplete;
170
-
171
- expect(el.opened).toBe(false);
172
- });
173
-
174
- it('does nothing when Escape is pressed while already closed', async () => {
175
- const el = await renderDrawer('<dile-app-drawer></dile-app-drawer>');
176
-
177
- window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
178
- await el.updateComplete;
179
-
180
- expect(el.opened).toBe(false);
181
- });
182
- });
183
-
184
- describe('Focus management', () => {
185
- it('moves focus into the menu when opened', async () => {
186
- const el = await renderDrawer('<dile-app-drawer><button>Item</button></dile-app-drawer>');
187
-
188
- el.open();
189
- await el.updateComplete;
190
-
191
- expect(el.shadowRoot.activeElement).toBe(el.shadowRoot.querySelector('.menu'));
192
- });
193
-
194
- it('restores focus to the trigger element after closing', async () => {
195
- document.body.innerHTML = '<button id="trigger">Open</button><dile-app-drawer></dile-app-drawer>';
196
- const trigger = document.querySelector('#trigger');
197
- const el = document.querySelector('dile-app-drawer');
198
- await el.updateComplete;
199
-
200
- trigger.focus();
201
- el.open();
202
- await el.updateComplete;
203
- el.close();
204
- await el.updateComplete;
205
- await wait(10);
206
-
207
- expect(document.activeElement).toBe(trigger);
208
- });
209
-
210
- it('traps Tab focus: wraps from the last focusable element to the first', async () => {
211
- const el = await renderDrawer(`
212
- <dile-app-drawer opened>
213
- <button id="first">First</button>
214
- <button id="last">Last</button>
215
- </dile-app-drawer>
216
- `);
217
- await el.updateComplete;
218
- const first = el.querySelector('#first');
219
- const last = el.querySelector('#last');
220
-
221
- last.focus();
222
- document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true, cancelable: true }));
223
-
224
- expect(document.activeElement).toBe(first);
225
- });
226
-
227
- it('traps Shift+Tab focus: wraps from the first focusable element to the last', async () => {
228
- const el = await renderDrawer(`
229
- <dile-app-drawer opened>
230
- <button id="first">First</button>
231
- <button id="last">Last</button>
232
- </dile-app-drawer>
233
- `);
234
- await el.updateComplete;
235
- const first = el.querySelector('#first');
236
- const last = el.querySelector('#last');
237
-
238
- first.focus();
239
- document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true, bubbles: true, cancelable: true }));
240
-
241
- expect(document.activeElement).toBe(last);
242
- });
243
-
244
- it('does not move focus when Tab is pressed with focus outside the drawer', async () => {
245
- document.body.innerHTML = `
246
- <input id="outside" />
247
- <dile-app-drawer opened>
248
- <button id="first">First</button>
249
- <button id="last">Last</button>
250
- </dile-app-drawer>
251
- `;
252
- const el = document.querySelector('dile-app-drawer');
253
- await el.updateComplete;
254
- const outside = document.querySelector('#outside');
255
-
256
- outside.focus();
257
- document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true, cancelable: true }));
258
-
259
- expect(document.activeElement).toBe(outside);
260
- });
261
- });
262
- });
@@ -1,25 +0,0 @@
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
- });