@dile/ui 3.17.1 → 3.18.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.
- package/components/steps/index.js +2 -0
- package/components/steps/src/DileStep.js +90 -0
- package/components/steps/src/DileSteps.js +171 -0
- package/components/steps/step.js +5 -0
- package/components/steps/steps.js +5 -0
- package/components/toast/src/DileToast.js +2 -1
- package/components/toast/src/DileToastPersistent.js +1 -1
- package/package.json +4 -4
- package/components/accordion/accordion.spec.js +0 -253
- package/components/app-drawer/app-drawer.spec.js +0 -262
- package/components/avatar/avatar.spec.js +0 -25
- 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
- package/components/bottom-nav/bottom-nav.spec.js +0 -340
- package/components/box-selector/box-selector-item.spec.js +0 -27
- package/components/box-selector/box-selector.spec.js +0 -27
- package/components/breadcrumbs/breadcrumbs-item.spec.js +0 -28
- package/components/breadcrumbs/breadcrumbs.spec.js +0 -33
- package/components/button/button.spec.js +0 -234
- package/components/card/card-slide.spec.js +0 -30
- package/components/card/card.spec.js +0 -26
- package/components/checkbox/checkbox.spec.js +0 -235
- package/components/chip/chip.spec.js +0 -31
- package/components/color-picker/color-picker.spec.js +0 -133
- package/components/confirm/confirm.spec.js +0 -57
- package/components/confirm-text/confirm-text.spec.js +0 -29
- package/components/copy-markdown-url/copy-markdown-url.spec.js +0 -27
- package/components/copy-text/copy-text.spec.js +0 -38
- 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
- package/components/data-grid/data-grid.spec.js +0 -421
- package/components/drop-file/drop-file.spec.js +0 -214
- package/components/drop-file/file-preview.spec.js +0 -35
- package/components/floating-feedback/floating-feedback.spec.js +0 -30
- package/components/floating-menu/floating-menu-item.spec.js +0 -27
- package/components/floating-menu/floating-menu.spec.js +0 -31
- package/components/form-separator/form-separator.spec.js +0 -28
- package/components/hamburger/hamburger.spec.js +0 -28
- package/components/icon/icon.spec.js +0 -31
- package/components/icon-switch/icon-switch-group.spec.js +0 -46
- package/components/icon-switch/icon-switch.spec.js +0 -35
- package/components/info-box/info-box.spec.js +0 -38
- package/components/inline-feedback/inline-feedback.spec.js +0 -46
- package/components/input/input-contact.spec.js +0 -32
- package/components/input/input-icon.spec.js +0 -166
- package/components/input/input-integer.spec.js +0 -31
- package/components/input/input-mask.spec.js +0 -314
- package/components/input/input-message.spec.js +0 -27
- package/components/input/input-money.spec.js +0 -30
- package/components/input/input-number-mask.spec.js +0 -48
- package/components/input/input-number.spec.js +0 -109
- package/components/input/input-percentage.spec.js +0 -31
- package/components/input/input-search.spec.js +0 -91
- package/components/input/input.spec.js +0 -47
- package/components/light-mode-switch/light-mode-switch.spec.js +0 -34
- package/components/menu-hamburger/menu-hamburger.spec.js +0 -34
- package/components/menu-overlay/menu-overlay.spec.js +0 -44
- package/components/message/message.spec.js +0 -42
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-closes-only-the-topmost-modal-when-Esc-is-pressed-1.png +0 -0
- 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
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-does-not-close-a-blocking-modal-when-Esc-is-pressed-1.png +0 -0
- 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
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-ignores-transitionend-events-coming-from-inner-elements-1.png +0 -0
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-keeps-the-focus-on-content-focused-by-the-user-when-opening-1.png +0 -0
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-opens-and-closes-through-the-opened-attribute-1.png +0 -0
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-reflects-the-opened-property-to-the-attribute-1.png +0 -0
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-releases-the-background-layer-after-the-close-animation-even-without-transitionend-1.png +0 -0
- package/components/modal/__screenshots__/modal.spec.js/dile-modal-releases-the-background-layer-immediately-when-the-animation-duration-is-0s-1.png +0 -0
- package/components/modal/modal-help.spec.js +0 -32
- package/components/modal/modal.spec.js +0 -250
- package/components/nav/nav.spec.js +0 -33
- package/components/number-picker/number-picker-element.spec.js +0 -30
- package/components/number-picker/number-picker.spec.js +0 -30
- 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
- package/components/order-switch/__screenshots__/order-switch.spec.js/dile-order-switch-clicking-the-label-toggles-the-order-when-selected-1.png +0 -0
- package/components/order-switch/__screenshots__/order-switch.spec.js/dile-order-switch-defaults-to-ascending-order-1.png +0 -0
- package/components/order-switch/order-switch.spec.js +0 -57
- package/components/otp-input/otp-input.spec.js +0 -41
- package/components/pages/pages.spec.js +0 -44
- package/components/password/password.spec.js +0 -35
- package/components/progress-bar/progress-bar.spec.js +0 -28
- 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
- package/components/radio-group/__screenshots__/radio-group-dialog.spec.js/dile-radio-group-dialog-renders-items-with-optional-description-1.png +0 -0
- 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
- package/components/radio-group/radio-group-dialog.spec.js +0 -126
- package/components/radio-group/radio-group.spec.js +0 -128
- package/components/radio-group/radio.spec.js +0 -32
- package/components/range/range.spec.js +0 -36
- package/components/rating/rating.spec.js +0 -34
- package/components/rating/star.spec.js +0 -30
- package/components/rating-scale-question/rating-scale-option.spec.js +0 -32
- package/components/rating-scale-question/rating-scale-question.spec.js +0 -35
- 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
- package/components/select/select-ajax-overlay.spec.js +0 -469
- package/components/select/select-overlay.spec.js +0 -612
- package/components/select/select-search.spec.js +0 -318
- package/components/select/select.spec.js +0 -574
- package/components/selector/selector-item.spec.js +0 -40
- package/components/selector/selector-overlay.spec.js +0 -139
- package/components/selector/selector.spec.js +0 -45
- package/components/slide-menu/slide-menu.spec.js +0 -38
- package/components/slide-show/slide-show.spec.js +0 -34
- package/components/social-icon/social-icon.spec.js +0 -26
- package/components/spinner/spinner-bar.spec.js +0 -26
- package/components/spinner/spinner-block.spec.js +0 -21
- package/components/spinner/spinner-horizontal.spec.js +0 -25
- package/components/spinner/spinner-icon.spec.js +0 -25
- package/components/spinner/spinner-modal.spec.js +0 -26
- package/components/spinner/spinner.spec.js +0 -26
- package/components/switch/switch.spec.js +0 -33
- package/components/tabs/tabs.spec.js +0 -85
- package/components/textarea/textarea.spec.js +0 -39
- package/components/time-picker/time-picker.spec.js +0 -26
- package/components/timer/timer.spec.js +0 -31
- package/components/toast/toast.spec.js +0 -86
- package/components/tooltip/chip-tooltip.spec.js +0 -29
- package/components/tooltip/tooltip.spec.js +0 -32
- package/components/web-visor/web-visor.spec.js +0 -32
- package/mixins/form/src/DileForm.spec.js +0 -29
|
@@ -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
|
-
});
|
|
Binary file
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
-
import '@dile/iconlib/lucide-icons/house.js';
|
|
3
|
-
import './bottom-nav.js';
|
|
4
|
-
|
|
5
|
-
describe('dile-bottom-nav', () => {
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
document.body.innerHTML = '';
|
|
8
|
-
document.documentElement.style.overflow = '';
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
async function renderNav(html) {
|
|
12
|
-
document.body.innerHTML = html;
|
|
13
|
-
const el = document.body.querySelector('dile-bottom-nav');
|
|
14
|
-
await el.updateComplete;
|
|
15
|
-
const items = [...el.querySelectorAll('dile-bottom-nav-item')];
|
|
16
|
-
await Promise.all(items.map((item) => item.updateComplete));
|
|
17
|
-
// Wait for slotchange to detect the sheets.
|
|
18
|
-
await new Promise((resolve) => setTimeout(resolve));
|
|
19
|
-
await Promise.all(items.map((item) => item.updateComplete));
|
|
20
|
-
return { el, items };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function trigger(item) {
|
|
24
|
-
return item.shadowRoot.querySelector('.trigger');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
it('renders a navigation landmark with the slotted items', async () => {
|
|
28
|
-
const { el, items } = await renderNav(`
|
|
29
|
-
<dile-bottom-nav label="App navigation">
|
|
30
|
-
<dile-bottom-nav-item label="Home"></dile-bottom-nav-item>
|
|
31
|
-
<dile-bottom-nav-item label="Search"></dile-bottom-nav-item>
|
|
32
|
-
</dile-bottom-nav>
|
|
33
|
-
`);
|
|
34
|
-
|
|
35
|
-
const nav = el.shadowRoot.querySelector('nav');
|
|
36
|
-
expect(nav.getAttribute('aria-label')).toBe('App navigation');
|
|
37
|
-
expect(items.length).toBe(2);
|
|
38
|
-
expect(items[0].shadowRoot.querySelector('.label').textContent).toBe('Home');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('is fixed by default and in the document flow when static', async () => {
|
|
42
|
-
const { el } = await renderNav(`<dile-bottom-nav></dile-bottom-nav>`);
|
|
43
|
-
expect(getComputedStyle(el).position).toBe('fixed');
|
|
44
|
-
|
|
45
|
-
el.static = true;
|
|
46
|
-
await el.updateComplete;
|
|
47
|
-
expect(getComputedStyle(el).position).toBe('relative');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('compensates the scrollbar width in the fixed bar while the page scroll is locked', async () => {
|
|
51
|
-
const { el } = await renderNav(`<dile-bottom-nav></dile-bottom-nav>`);
|
|
52
|
-
document.documentElement.style.setProperty('--dile-bottom-nav-scrollbar-width', '15px');
|
|
53
|
-
|
|
54
|
-
expect(getComputedStyle(el).paddingRight).toBe('15px');
|
|
55
|
-
el.static = true;
|
|
56
|
-
await el.updateComplete;
|
|
57
|
-
expect(getComputedStyle(el).paddingRight).toBe('0px');
|
|
58
|
-
|
|
59
|
-
document.documentElement.style.removeProperty('--dile-bottom-nav-scrollbar-width');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('renders the icon of the item from a dile-iconlib string', async () => {
|
|
63
|
-
const { items } = await renderNav(`
|
|
64
|
-
<dile-bottom-nav>
|
|
65
|
-
<dile-bottom-nav-item label="Home" icon="lucide.house"></dile-bottom-nav-item>
|
|
66
|
-
<dile-bottom-nav-item label="Search"></dile-bottom-nav-item>
|
|
67
|
-
</dile-bottom-nav>
|
|
68
|
-
`);
|
|
69
|
-
|
|
70
|
-
const icon = items[0].shadowRoot.querySelector('dile-iconlib');
|
|
71
|
-
expect(icon.getAttribute('icon')).toBe('lucide.house');
|
|
72
|
-
expect(items[1].shadowRoot.querySelector('dile-iconlib')).toBeNull();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('marks the selected item by attribute and changes selection on click', async () => {
|
|
76
|
-
const { el, items } = await renderNav(`
|
|
77
|
-
<dile-bottom-nav attrForSelected="name" selected="home">
|
|
78
|
-
<dile-bottom-nav-item name="home" label="Home"></dile-bottom-nav-item>
|
|
79
|
-
<dile-bottom-nav-item name="search" label="Search"></dile-bottom-nav-item>
|
|
80
|
-
</dile-bottom-nav>
|
|
81
|
-
`);
|
|
82
|
-
|
|
83
|
-
expect(items[0].selected).toBe(true);
|
|
84
|
-
expect(trigger(items[0]).getAttribute('aria-current')).toBe('page');
|
|
85
|
-
|
|
86
|
-
let detail;
|
|
87
|
-
el.addEventListener('dile-selected-changed', (e) => { detail = e.detail; });
|
|
88
|
-
trigger(items[1]).click();
|
|
89
|
-
await el.updateComplete;
|
|
90
|
-
|
|
91
|
-
expect(el.selected).toBe('search');
|
|
92
|
-
expect(detail.selected).toBe('search');
|
|
93
|
-
expect(items[1].selected).toBe(true);
|
|
94
|
-
expect(items[0].selected).toBe(false);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('dispatches dile-bottom-nav-item-click with the item name', async () => {
|
|
98
|
-
const { el, items } = await renderNav(`
|
|
99
|
-
<dile-bottom-nav>
|
|
100
|
-
<dile-bottom-nav-item name="home" label="Home"></dile-bottom-nav-item>
|
|
101
|
-
</dile-bottom-nav>
|
|
102
|
-
`);
|
|
103
|
-
|
|
104
|
-
let detail;
|
|
105
|
-
el.addEventListener('dile-bottom-nav-item-click', (e) => { detail = e.detail; });
|
|
106
|
-
trigger(items[0]).click();
|
|
107
|
-
|
|
108
|
-
expect(detail.name).toBe('home');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('renders a native link when the item has an href', async () => {
|
|
112
|
-
const { items } = await renderNav(`
|
|
113
|
-
<dile-bottom-nav>
|
|
114
|
-
<dile-bottom-nav-item label="Home" href="#home" target="_self"></dile-bottom-nav-item>
|
|
115
|
-
<dile-bottom-nav-item label="Search"></dile-bottom-nav-item>
|
|
116
|
-
</dile-bottom-nav>
|
|
117
|
-
`);
|
|
118
|
-
|
|
119
|
-
const link = items[0].shadowRoot.querySelector('a');
|
|
120
|
-
expect(link.getAttribute('href')).toBe('#home');
|
|
121
|
-
expect(link.getAttribute('target')).toBe('_self');
|
|
122
|
-
expect(items[1].shadowRoot.querySelector('a')).toBeNull();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('opens the bottom sheet of an item with slotted content without changing the selection', async () => {
|
|
126
|
-
const { el, items } = await renderNav(`
|
|
127
|
-
<dile-bottom-nav attrForSelected="name" selected="home">
|
|
128
|
-
<dile-bottom-nav-item name="home" label="Home"></dile-bottom-nav-item>
|
|
129
|
-
<dile-bottom-nav-item name="create" label="Create">
|
|
130
|
-
<a href="#new">New</a>
|
|
131
|
-
</dile-bottom-nav-item>
|
|
132
|
-
</dile-bottom-nav>
|
|
133
|
-
`);
|
|
134
|
-
|
|
135
|
-
const button = trigger(items[1]);
|
|
136
|
-
expect(button.getAttribute('aria-haspopup')).toBe('dialog');
|
|
137
|
-
expect(button.getAttribute('aria-expanded')).toBe('false');
|
|
138
|
-
|
|
139
|
-
let opened = false;
|
|
140
|
-
el.addEventListener('dile-bottom-nav-sheet-opened', () => { opened = true; });
|
|
141
|
-
button.click();
|
|
142
|
-
await items[1].updateComplete;
|
|
143
|
-
|
|
144
|
-
expect(items[1].opened).toBe(true);
|
|
145
|
-
expect(opened).toBe(true);
|
|
146
|
-
expect(button.getAttribute('aria-expanded')).toBe('true');
|
|
147
|
-
expect(document.documentElement.style.overflow).toBe('hidden');
|
|
148
|
-
expect(el.selected).toBe('home');
|
|
149
|
-
expect(items[0].selected).toBe(true);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('closes the sheet when clicking the overlay', async () => {
|
|
153
|
-
const { items } = await renderNav(`
|
|
154
|
-
<dile-bottom-nav>
|
|
155
|
-
<dile-bottom-nav-item label="Create"><p>Content</p></dile-bottom-nav-item>
|
|
156
|
-
</dile-bottom-nav>
|
|
157
|
-
`);
|
|
158
|
-
|
|
159
|
-
await items[0].open();
|
|
160
|
-
let closed = false;
|
|
161
|
-
items[0].addEventListener('dile-bottom-nav-sheet-closed', () => { closed = true; });
|
|
162
|
-
items[0].shadowRoot.querySelector('.overlay').click();
|
|
163
|
-
await items[0].updateComplete;
|
|
164
|
-
|
|
165
|
-
expect(items[0].opened).toBe(false);
|
|
166
|
-
expect(closed).toBe(true);
|
|
167
|
-
expect(document.documentElement.style.overflow).toBe('');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('closes the sheet when pressing Escape', async () => {
|
|
171
|
-
const { items } = await renderNav(`
|
|
172
|
-
<dile-bottom-nav>
|
|
173
|
-
<dile-bottom-nav-item label="Create"><p>Content</p></dile-bottom-nav-item>
|
|
174
|
-
</dile-bottom-nav>
|
|
175
|
-
`);
|
|
176
|
-
|
|
177
|
-
await items[0].open();
|
|
178
|
-
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
179
|
-
await items[0].updateComplete;
|
|
180
|
-
|
|
181
|
-
expect(items[0].opened).toBe(false);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('closes the sheet when clicking an element with data-close', async () => {
|
|
185
|
-
const { items } = await renderNav(`
|
|
186
|
-
<dile-bottom-nav>
|
|
187
|
-
<dile-bottom-nav-item label="Create">
|
|
188
|
-
<button id="keep">Keep</button>
|
|
189
|
-
<button id="close" data-close>Close</button>
|
|
190
|
-
</dile-bottom-nav-item>
|
|
191
|
-
</dile-bottom-nav>
|
|
192
|
-
`);
|
|
193
|
-
|
|
194
|
-
await items[0].open();
|
|
195
|
-
document.getElementById('keep').click();
|
|
196
|
-
await items[0].updateComplete;
|
|
197
|
-
expect(items[0].opened).toBe(true);
|
|
198
|
-
|
|
199
|
-
document.getElementById('close').click();
|
|
200
|
-
await items[0].updateComplete;
|
|
201
|
-
expect(items[0].opened).toBe(false);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('closes the sheet when clicking any link or button with close-on-click', async () => {
|
|
205
|
-
const { items } = await renderNav(`
|
|
206
|
-
<dile-bottom-nav>
|
|
207
|
-
<dile-bottom-nav-item label="Create" close-on-click>
|
|
208
|
-
<button id="action">Action</button>
|
|
209
|
-
</dile-bottom-nav-item>
|
|
210
|
-
</dile-bottom-nav>
|
|
211
|
-
`);
|
|
212
|
-
|
|
213
|
-
await items[0].open();
|
|
214
|
-
document.getElementById('action').click();
|
|
215
|
-
await items[0].updateComplete;
|
|
216
|
-
|
|
217
|
-
expect(items[0].opened).toBe(false);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('keeps only one sheet opened at a time', async () => {
|
|
221
|
-
const { items } = await renderNav(`
|
|
222
|
-
<dile-bottom-nav>
|
|
223
|
-
<dile-bottom-nav-item label="One"><p>One</p></dile-bottom-nav-item>
|
|
224
|
-
<dile-bottom-nav-item label="Two"><p>Two</p></dile-bottom-nav-item>
|
|
225
|
-
</dile-bottom-nav>
|
|
226
|
-
`);
|
|
227
|
-
|
|
228
|
-
await items[0].open();
|
|
229
|
-
await items[1].open();
|
|
230
|
-
await items[0].updateComplete;
|
|
231
|
-
|
|
232
|
-
expect(items[0].opened).toBe(false);
|
|
233
|
-
expect(items[1].opened).toBe(true);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('replaces the item icon with the close icon while the sheet is opened', async () => {
|
|
237
|
-
const { items } = await renderNav(`
|
|
238
|
-
<dile-bottom-nav>
|
|
239
|
-
<dile-bottom-nav-item label="Create"><p>Content</p></dile-bottom-nav-item>
|
|
240
|
-
</dile-bottom-nav>
|
|
241
|
-
`);
|
|
242
|
-
|
|
243
|
-
expect(items[0].shadowRoot.querySelector('.close-icon')).toBeNull();
|
|
244
|
-
await items[0].open();
|
|
245
|
-
expect(items[0].shadowRoot.querySelector('.close-icon dile-iconlib').getAttribute('icon')).toBe('lucide.x');
|
|
246
|
-
expect(items[0].shadowRoot.querySelector('.trigger').textContent).toContain('Create');
|
|
247
|
-
|
|
248
|
-
trigger(items[0]).click();
|
|
249
|
-
await items[0].updateComplete;
|
|
250
|
-
expect(items[0].opened).toBe(false);
|
|
251
|
-
expect(items[0].shadowRoot.querySelector('.close-icon')).toBeNull();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it('places the sheet and the overlay above the bar', async () => {
|
|
255
|
-
const { el, items } = await renderNav(`
|
|
256
|
-
<dile-bottom-nav>
|
|
257
|
-
<dile-bottom-nav-item label="Create"><p>Content</p></dile-bottom-nav-item>
|
|
258
|
-
</dile-bottom-nav>
|
|
259
|
-
`);
|
|
260
|
-
|
|
261
|
-
await items[0].open();
|
|
262
|
-
await items[0].updateComplete;
|
|
263
|
-
const barTop = el.getBoundingClientRect().top;
|
|
264
|
-
const barHeight = window.innerHeight - barTop;
|
|
265
|
-
// Read the computed bottom instead of the bounding box, which is affected by the opening transition.
|
|
266
|
-
const sheetBottom = parseFloat(getComputedStyle(items[0].shadowRoot.querySelector('.sheet')).bottom);
|
|
267
|
-
const overlay = items[0].shadowRoot.querySelector('.overlay').getBoundingClientRect();
|
|
268
|
-
|
|
269
|
-
expect(sheetBottom).toBeGreaterThanOrEqual(Math.floor(barHeight));
|
|
270
|
-
expect(Math.round(overlay.bottom)).toBe(Math.round(barTop));
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
describe('static bar', () => {
|
|
274
|
-
async function renderStatic() {
|
|
275
|
-
const result = await renderNav(`
|
|
276
|
-
<div style="height: 400px"></div>
|
|
277
|
-
<dile-bottom-nav static style="width: 300px; margin: 0 auto;">
|
|
278
|
-
<dile-bottom-nav-item label="Home"></dile-bottom-nav-item>
|
|
279
|
-
<dile-bottom-nav-item label="Create"><button id="inside">Inside</button></dile-bottom-nav-item>
|
|
280
|
-
</dile-bottom-nav>
|
|
281
|
-
<button id="outside">Outside</button>
|
|
282
|
-
`);
|
|
283
|
-
await result.items[1].open();
|
|
284
|
-
await result.items[1].updateComplete;
|
|
285
|
-
return result;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
it('opens a floating sheet without overlay and without locking the page scroll', async () => {
|
|
289
|
-
const { items } = await renderStatic();
|
|
290
|
-
const overlay = items[1].shadowRoot.querySelector('.overlay');
|
|
291
|
-
const sheet = items[1].shadowRoot.querySelector('.sheet');
|
|
292
|
-
|
|
293
|
-
expect(getComputedStyle(overlay).display).toBe('none');
|
|
294
|
-
expect(sheet.classList.contains('floating')).toBe(true);
|
|
295
|
-
expect(sheet.getAttribute('aria-modal')).toBe('false');
|
|
296
|
-
expect(document.documentElement.style.overflow).toBe('');
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it('places the floating sheet above the trigger, centered and inside the viewport', async () => {
|
|
300
|
-
const { items } = await renderStatic();
|
|
301
|
-
const sheet = items[1].shadowRoot.querySelector('.sheet');
|
|
302
|
-
const triggerRect = trigger(items[1]).getBoundingClientRect();
|
|
303
|
-
// Inline position set by DileOverlay, not affected by the opening transition.
|
|
304
|
-
const top = parseFloat(sheet.style.top);
|
|
305
|
-
const left = parseFloat(sheet.style.left);
|
|
306
|
-
const width = sheet.offsetWidth;
|
|
307
|
-
|
|
308
|
-
expect(top + sheet.offsetHeight).toBeLessThanOrEqual(triggerRect.top);
|
|
309
|
-
expect(left).toBeGreaterThanOrEqual(10);
|
|
310
|
-
expect(left + width).toBeLessThanOrEqual(document.documentElement.clientWidth - 10);
|
|
311
|
-
const centered = triggerRect.left + triggerRect.width / 2 - width / 2;
|
|
312
|
-
const clamped = Math.min(Math.max(centered, 10), document.documentElement.clientWidth - width - 15);
|
|
313
|
-
expect(Math.round(left)).toBe(Math.round(clamped));
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it('closes the floating sheet when clicking outside, but not inside', async () => {
|
|
317
|
-
const { items } = await renderStatic();
|
|
318
|
-
|
|
319
|
-
document.getElementById('inside').click();
|
|
320
|
-
await items[1].updateComplete;
|
|
321
|
-
expect(items[1].opened).toBe(true);
|
|
322
|
-
|
|
323
|
-
document.getElementById('outside').click();
|
|
324
|
-
await items[1].updateComplete;
|
|
325
|
-
expect(items[1].opened).toBe(false);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it('toggles the floating sheet from the trigger', async () => {
|
|
329
|
-
const { items } = await renderStatic();
|
|
330
|
-
|
|
331
|
-
trigger(items[1]).click();
|
|
332
|
-
await items[1].updateComplete;
|
|
333
|
-
expect(items[1].opened).toBe(false);
|
|
334
|
-
|
|
335
|
-
trigger(items[1]).click();
|
|
336
|
-
await items[1].updateComplete;
|
|
337
|
-
expect(items[1].opened).toBe(true);
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
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,27 +0,0 @@
|
|
|
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
|
-
});
|