@adia-ai/web-components 0.8.19 → 0.8.21
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/CHANGELOG.md +26 -0
- package/components/card/card.a2ui.json +6 -3
- package/components/card/card.css +15 -1
- package/components/card/card.yaml +5 -3
- package/components/nav-group/nav-group.a2ui.json +5 -0
- package/components/nav-group/nav-group.class.js +14 -6
- package/components/nav-group/nav-group.css +5 -2
- package/components/nav-group/nav-group.d.ts +2 -0
- package/components/nav-group/nav-group.yaml +4 -0
- package/components/select/select.a2ui.json +3 -2
- package/components/select/select.class.js +14 -1
- package/components/select/select.css +13 -1
- package/components/select/select.yaml +5 -4
- package/components/table/table.class.js +7 -3
- package/components/table/table.css +7 -5
- package/dist/theme-provider.min.js +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +19 -19
- package/dist/web-components.sheet.js +1 -1
- package/package.json +2 -1
- package/components/adia-mark/adia-mark.test.js +0 -69
- package/components/adia-wordmark/adia-wordmark.test.js +0 -69
- package/components/agent-artifact/agent-artifact.test.js +0 -61
- package/components/alert/alert.test.js +0 -364
- package/components/color-picker/color-picker.test.js +0 -96
- package/components/combobox/combobox.test.js +0 -256
- package/components/command/command.test.js +0 -99
- package/components/date-range-picker/date-range-picker.test.js +0 -387
- package/components/datetime-picker/datetime-picker.test.js +0 -454
- package/components/demo-toggle/demo-toggle.test.js +0 -102
- package/components/description-list/description-list.test.js +0 -72
- package/components/display-field/display-field.test.js +0 -115
- package/components/empty-state/empty-state.test.js +0 -77
- package/components/field/field.test.js +0 -260
- package/components/fields/fields.test.js +0 -88
- package/components/inline-message/inline-message.test.js +0 -203
- package/components/input/input.test.js +0 -335
- package/components/integration-card/integration-card.test.js +0 -318
- package/components/link/link.test.js +0 -143
- package/components/list/list.test.js +0 -106
- package/components/list-window/list-window.test.js +0 -303
- package/components/loading-overlay/loading-overlay.test.js +0 -257
- package/components/menu/menu.test.js +0 -176
- package/components/nav/nav.test.js +0 -428
- package/components/nav-group/nav-group.test.js +0 -67
- package/components/option-card/option-card.test.js +0 -32
- package/components/pagination/pagination.test.js +0 -195
- package/components/select/select.test.js +0 -481
- package/components/slider/slider.test.js +0 -202
- package/components/spinner/spinner.test.js +0 -272
- package/components/stat/stat.test.js +0 -108
- package/components/step-progress/step-progress.test.js +0 -129
- package/components/swatch/swatch.test.js +0 -563
- package/components/table/table.test.js +0 -479
- package/components/table-toolbar/table-toolbar.test.js +0 -152
- package/components/tag/tag.test.js +0 -137
- package/components/tags-input/tags-input.test.js +0 -477
- package/components/text/text.test.js +0 -202
- package/components/theme-provider/theme-provider.test.js +0 -94
- package/components/time-picker/time-picker.test.js +0 -287
- package/components/timeline/timeline.test.js +0 -31
- package/components/toggle-scheme/toggle-scheme.test.js +0 -110
- package/components/tree/tree.test.js +0 -312
- package/core/element.test.js +0 -389
- package/core/icons.test.js +0 -187
- package/core/logical-children.test.js +0 -54
- package/core/markdown.test.js +0 -47
- package/core/responsive.test.js +0 -121
- package/core/template.test.js +0 -611
- package/traits/active-state/active-state.test.js +0 -28
- package/traits/anchor-positioning/anchor-positioning.test.js +0 -122
- package/traits/announcer/announcer.test.js +0 -268
- package/traits/arrow-grid-nav/arrow-grid-nav.test.js +0 -375
- package/traits/attention-pulse/attention-pulse.test.js +0 -26
- package/traits/confetti/confetti.test.js +0 -46
- package/traits/confetti-burst/confetti-burst.test.js +0 -46
- package/traits/count-up/count-up.test.js +0 -54
- package/traits/declarative.test.js +0 -138
- package/traits/dirty-state/dirty-state.test.js +0 -45
- package/traits/drag-ghost/drag-ghost.test.js +0 -19
- package/traits/draggable/draggable.test.js +0 -60
- package/traits/draggable-list-item/draggable-list-item.test.js +0 -51
- package/traits/drop-target/drop-target.test.js +0 -241
- package/traits/droppable/droppable.test.js +0 -54
- package/traits/droppable-collection/droppable-collection.test.js +0 -99
- package/traits/error-shake/error-shake.test.js +0 -114
- package/traits/fade-presence/fade-presence.test.js +0 -20
- package/traits/focus-restore/focus-restore.test.js +0 -202
- package/traits/focus-trap/focus-trap.test.js +0 -42
- package/traits/focusable/focusable.test.js +0 -53
- package/traits/glow-focus/glow-focus.test.js +0 -31
- package/traits/gradient-shift/gradient-shift.test.js +0 -22
- package/traits/haptic-feedback/haptic-feedback.test.js +0 -52
- package/traits/hotkey/hotkey.test.js +0 -61
- package/traits/hoverable/hoverable.test.js +0 -24
- package/traits/inertia-drag/inertia-drag.test.js +0 -33
- package/traits/input-mask/input-mask.test.js +0 -151
- package/traits/intersection-observer/intersection-observer.test.js +0 -38
- package/traits/keyboard-nav/keyboard-nav.test.js +0 -41
- package/traits/keyboard-reorderable/keyboard-reorderable.test.js +0 -45
- package/traits/layout-animation/layout-animation.test.js +0 -114
- package/traits/long-press/long-press.test.js +0 -244
- package/traits/magnetic-hover/magnetic-hover.test.js +0 -30
- package/traits/noise-texture/noise-texture.test.js +0 -20
- package/traits/parallax/parallax.test.js +0 -26
- package/traits/portal/portal.test.js +0 -30
- package/traits/pressable/pressable.test.js +0 -73
- package/traits/resettable/resettable.test.js +0 -100
- package/traits/resizable/resizable.test.js +0 -39
- package/traits/resize-observer/resize-observer.test.js +0 -38
- package/traits/ripple/ripple.test.js +0 -32
- package/traits/roving-tabindex/roving-tabindex.test.js +0 -28
- package/traits/scale-press/scale-press.test.js +0 -39
- package/traits/scroll-lock/scroll-lock.test.js +0 -45
- package/traits/scroll-progress/scroll-progress.test.js +0 -182
- package/traits/shimmer-loading/shimmer-loading.test.js +0 -43
- package/traits/smoke.test.js +0 -111
- package/traits/snap-to-grid/snap-to-grid.test.js +0 -40
- package/traits/sound-feedback/sound-feedback.test.js +0 -26
- package/traits/spring-animate/spring-animate.test.js +0 -28
- package/traits/success-checkmark/success-checkmark.test.js +0 -120
- package/traits/tilt-hover/tilt-hover.test.js +0 -32
- package/traits/tossable/tossable.test.js +0 -31
- package/traits/traits-host.test.js +0 -73
- package/traits/typeahead/typeahead.test.js +0 -38
- package/traits/typewriter/typewriter.test.js +0 -47
- package/traits/validation/validation.test.js +0 -93
- package/traits/view-transition/view-transition.test.js +0 -268
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* date-range-picker-ui — unit tests covering the SPEC-037 contract.
|
|
3
|
-
*
|
|
4
|
-
* Run under happy-dom (per vitest.config.js). showPopover/hidePopover are
|
|
5
|
-
* stubbed to no-ops by `packages/web-components/test-setup.js`, so popover
|
|
6
|
-
* state is tracked via the `open` reflected attribute and the `<popover>`
|
|
7
|
-
* div's presence, not via `:popover-open` selector matching.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
11
|
-
import '../../core/element.js';
|
|
12
|
-
import '../button/button.js';
|
|
13
|
-
import '../popover/popover.js';
|
|
14
|
-
import '../calendar-picker/calendar-picker.js';
|
|
15
|
-
import '../divider/divider.js';
|
|
16
|
-
import './date-range-picker.js';
|
|
17
|
-
|
|
18
|
-
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
19
|
-
|
|
20
|
-
function mount(html) {
|
|
21
|
-
const wrap = document.createElement('div');
|
|
22
|
-
wrap.innerHTML = html.trim();
|
|
23
|
-
document.body.appendChild(wrap);
|
|
24
|
-
return wrap.firstElementChild;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe('date-range-picker-ui — structure + defaults', () => {
|
|
28
|
-
beforeEach(() => { document.body.innerHTML = ''; });
|
|
29
|
-
|
|
30
|
-
it('registers the custom element', () => {
|
|
31
|
-
expect(customElements.get('date-range-picker-ui')).toBeTruthy();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('stamps a default <button-ui slot="trigger"> when no slot="trigger" is authored', async () => {
|
|
35
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
36
|
-
await tick();
|
|
37
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
38
|
-
expect(trigger).not.toBeNull();
|
|
39
|
-
expect(trigger.localName).toBe('button-ui');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('stamps a popover container with role=dialog', async () => {
|
|
43
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
44
|
-
await tick();
|
|
45
|
-
const popover = el.querySelector(':scope > [slot="popover"]');
|
|
46
|
-
expect(popover).not.toBeNull();
|
|
47
|
-
expect(popover.getAttribute('role')).toBe('dialog');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('stamps TWO <calendar-grid-ui> instances inside the popover', async () => {
|
|
51
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
52
|
-
await tick();
|
|
53
|
-
const calArea = el.querySelector(':scope > [slot="popover"] > [data-calendar-area]');
|
|
54
|
-
expect(calArea).not.toBeNull();
|
|
55
|
-
const cals = calArea.querySelectorAll(':scope > calendar-grid-ui');
|
|
56
|
-
expect(cals.length).toBe(2);
|
|
57
|
-
expect(cals[0].hasAttribute('data-cal-from')).toBe(true);
|
|
58
|
-
expect(cals[1].hasAttribute('data-cal-to')).toBe(true);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('stamps a preset rail with the default 5 presets', async () => {
|
|
62
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
63
|
-
await tick();
|
|
64
|
-
const rail = el.querySelector(':scope > [slot="popover"] > [data-preset-rail]');
|
|
65
|
-
expect(rail).not.toBeNull();
|
|
66
|
-
const buttons = rail.querySelectorAll('button-ui[data-preset-label]');
|
|
67
|
-
expect(buttons.length).toBe(5);
|
|
68
|
-
expect(buttons[0].dataset.presetLabel).toBe('Today');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('host carries role=combobox + aria-haspopup=dialog', async () => {
|
|
72
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
73
|
-
await tick();
|
|
74
|
-
expect(el.getAttribute('role')).toBe('combobox');
|
|
75
|
-
expect(el.getAttribute('aria-haspopup')).toBe('dialog');
|
|
76
|
-
expect(el.getAttribute('aria-expanded')).toBe('false');
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe('date-range-picker-ui — props + reflection', () => {
|
|
81
|
-
beforeEach(() => { document.body.innerHTML = ''; });
|
|
82
|
-
|
|
83
|
-
it('reflects [min] and [max] onto both calendar panes', async () => {
|
|
84
|
-
const el = mount('<date-range-picker-ui min="2026-01-01" max="2026-12-31"></date-range-picker-ui>');
|
|
85
|
-
await tick();
|
|
86
|
-
const cals = el.querySelectorAll('calendar-grid-ui');
|
|
87
|
-
expect(cals[0].getAttribute('min')).toBe('2026-01-01');
|
|
88
|
-
expect(cals[0].getAttribute('max')).toBe('2026-12-31');
|
|
89
|
-
expect(cals[1].getAttribute('min')).toBe('2026-01-01');
|
|
90
|
-
expect(cals[1].getAttribute('max')).toBe('2026-12-31');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('parses JSON `value` into rangeValue', async () => {
|
|
94
|
-
const el = mount('<date-range-picker-ui value=\'{"from":"2026-01-01","to":"2026-01-07"}\'></date-range-picker-ui>');
|
|
95
|
-
await tick();
|
|
96
|
-
expect(el.rangeValue).toEqual({ from: '2026-01-01', to: '2026-01-07' });
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('renders formatted-range text in the trigger when value is set', async () => {
|
|
100
|
-
const el = mount('<date-range-picker-ui value=\'{"from":"2026-01-01","to":"2026-01-07"}\'></date-range-picker-ui>');
|
|
101
|
-
await tick();
|
|
102
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
103
|
-
expect(trigger.getAttribute('text')).toContain('Jan 1, 2026');
|
|
104
|
-
expect(trigger.getAttribute('text')).toContain('Jan 7, 2026');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('uses [placeholder] in the trigger when value is empty', async () => {
|
|
108
|
-
const el = mount('<date-range-picker-ui placeholder="Choose range"></date-range-picker-ui>');
|
|
109
|
-
await tick();
|
|
110
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
111
|
-
expect(trigger.getAttribute('text')).toBe('Choose range');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('reflects [comparison] as an attribute', async () => {
|
|
115
|
-
const el = mount('<date-range-picker-ui comparison></date-range-picker-ui>');
|
|
116
|
-
await tick();
|
|
117
|
-
expect(el.hasAttribute('comparison')).toBe(true);
|
|
118
|
-
expect(el.comparison).toBe(true);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('[no-presets] removes the preset rail', async () => {
|
|
122
|
-
const el = mount('<date-range-picker-ui no-presets></date-range-picker-ui>');
|
|
123
|
-
await tick();
|
|
124
|
-
expect(el.querySelector(':scope > [slot="popover"] > [data-preset-rail]')).toBeNull();
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('custom `presets` property overrides the default list', async () => {
|
|
128
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
129
|
-
await tick();
|
|
130
|
-
el.presets = [
|
|
131
|
-
{ label: 'Q1', range: { from: '2026-01-01', to: '2026-03-31' } },
|
|
132
|
-
{ label: 'Q2', range: { from: '2026-04-01', to: '2026-06-30' } },
|
|
133
|
-
];
|
|
134
|
-
await tick();
|
|
135
|
-
const rail = el.querySelector(':scope > [slot="popover"] > [data-preset-rail]');
|
|
136
|
-
const buttons = rail.querySelectorAll('button-ui[data-preset-label]');
|
|
137
|
-
expect(buttons.length).toBe(2);
|
|
138
|
-
expect(buttons[0].dataset.presetLabel).toBe('Q1');
|
|
139
|
-
expect(buttons[1].dataset.presetLabel).toBe('Q2');
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe('date-range-picker-ui — open/close behavior', () => {
|
|
144
|
-
beforeEach(() => { document.body.innerHTML = ''; });
|
|
145
|
-
|
|
146
|
-
it('opens on trigger click + emits `open` event', async () => {
|
|
147
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
148
|
-
await tick();
|
|
149
|
-
let openDetail = null;
|
|
150
|
-
el.addEventListener('open', (e) => { openDetail = e.detail; });
|
|
151
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
152
|
-
trigger.click();
|
|
153
|
-
await tick();
|
|
154
|
-
expect(el.open).toBe(true);
|
|
155
|
-
expect(el.hasAttribute('open')).toBe(true);
|
|
156
|
-
expect(el.getAttribute('aria-expanded')).toBe('true');
|
|
157
|
-
expect(openDetail?.trigger).toBe('click');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('closes on second trigger click + emits `close`', async () => {
|
|
161
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
162
|
-
await tick();
|
|
163
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
164
|
-
let closeDetail = null;
|
|
165
|
-
el.addEventListener('close', (e) => { closeDetail = e.detail; });
|
|
166
|
-
trigger.click();
|
|
167
|
-
await tick();
|
|
168
|
-
trigger.click();
|
|
169
|
-
await tick();
|
|
170
|
-
expect(el.open).toBe(false);
|
|
171
|
-
expect(closeDetail).not.toBeNull();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('Escape closes the popover + emits close with reason "escape"', async () => {
|
|
175
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
176
|
-
await tick();
|
|
177
|
-
el.open = true;
|
|
178
|
-
await tick();
|
|
179
|
-
let reason = null;
|
|
180
|
-
el.addEventListener('close', (e) => { reason = e.detail?.reason; });
|
|
181
|
-
const popover = el.querySelector(':scope > [slot="popover"]');
|
|
182
|
-
const ev = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true });
|
|
183
|
-
popover.dispatchEvent(ev);
|
|
184
|
-
await tick();
|
|
185
|
-
expect(el.open).toBe(false);
|
|
186
|
-
expect(reason).toBe('escape');
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('Enter on the trigger opens the popover with trigger=keyboard', async () => {
|
|
190
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
191
|
-
await tick();
|
|
192
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
193
|
-
let openDetail = null;
|
|
194
|
-
el.addEventListener('open', (e) => { openDetail = e.detail; });
|
|
195
|
-
trigger.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true }));
|
|
196
|
-
await tick();
|
|
197
|
-
expect(el.open).toBe(true);
|
|
198
|
-
expect(openDetail?.trigger).toBe('keyboard');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('[disabled] blocks the trigger from opening', async () => {
|
|
202
|
-
const el = mount('<date-range-picker-ui disabled></date-range-picker-ui>');
|
|
203
|
-
await tick();
|
|
204
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
205
|
-
trigger.click();
|
|
206
|
-
await tick();
|
|
207
|
-
expect(el.open).toBe(false);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('[readonly] blocks the popover from opening', async () => {
|
|
211
|
-
const el = mount('<date-range-picker-ui readonly></date-range-picker-ui>');
|
|
212
|
-
await tick();
|
|
213
|
-
const trigger = el.querySelector(':scope > [slot="trigger"]');
|
|
214
|
-
trigger.click();
|
|
215
|
-
await tick();
|
|
216
|
-
expect(el.open).toBe(false);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
describe('date-range-picker-ui — selection + commit', () => {
|
|
221
|
-
beforeEach(() => { document.body.innerHTML = ''; });
|
|
222
|
-
|
|
223
|
-
it('clicking a preset commits value + closes popover + fires `change` with presetLabel', async () => {
|
|
224
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
225
|
-
await tick();
|
|
226
|
-
el.open = true;
|
|
227
|
-
await tick();
|
|
228
|
-
let detail = null;
|
|
229
|
-
el.addEventListener('change', (e) => { detail = e.detail; });
|
|
230
|
-
const rail = el.querySelector(':scope > [slot="popover"] > [data-preset-rail]');
|
|
231
|
-
const todayBtn = rail.querySelector('button-ui[data-preset-label="Today"]');
|
|
232
|
-
todayBtn.click();
|
|
233
|
-
await tick();
|
|
234
|
-
expect(detail).not.toBeNull();
|
|
235
|
-
expect(detail.presetLabel).toBe('Today');
|
|
236
|
-
expect(detail.value.from).toBe(detail.value.to);
|
|
237
|
-
expect(el.open).toBe(false);
|
|
238
|
-
expect(el.rangeValue).not.toBeNull();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('applyPreset(label) commits the matching preset', async () => {
|
|
242
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
243
|
-
await tick();
|
|
244
|
-
let detail = null;
|
|
245
|
-
el.addEventListener('change', (e) => { detail = e.detail; });
|
|
246
|
-
const result = el.applyPreset('Last 7 days');
|
|
247
|
-
expect(result).toBe(true);
|
|
248
|
-
await tick();
|
|
249
|
-
expect(detail).not.toBeNull();
|
|
250
|
-
expect(detail.presetLabel).toBe('Last 7 days');
|
|
251
|
-
expect(detail.value.from < detail.value.to).toBe(true);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it('applyPreset(unknown) returns false and does not commit', async () => {
|
|
255
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
256
|
-
await tick();
|
|
257
|
-
let fired = false;
|
|
258
|
-
el.addEventListener('change', () => { fired = true; });
|
|
259
|
-
expect(el.applyPreset('NonExistent')).toBe(false);
|
|
260
|
-
expect(fired).toBe(false);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('emits `input` (partial state) when only `from` is selected', async () => {
|
|
264
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
265
|
-
await tick();
|
|
266
|
-
let inputDetail = null;
|
|
267
|
-
let changeFired = false;
|
|
268
|
-
el.addEventListener('input', (e) => { inputDetail = e.detail; });
|
|
269
|
-
el.addEventListener('change', () => { changeFired = true; });
|
|
270
|
-
const calFrom = el.querySelector('calendar-grid-ui[data-cal-from]');
|
|
271
|
-
calFrom.value = '2026-03-15';
|
|
272
|
-
calFrom.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '2026-03-15' } }));
|
|
273
|
-
await tick();
|
|
274
|
-
expect(inputDetail).not.toBeNull();
|
|
275
|
-
expect(inputDetail.value.from).toBe('2026-03-15');
|
|
276
|
-
expect(inputDetail.value.to).toBeNull();
|
|
277
|
-
expect(changeFired).toBe(false);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it('commits `change` when both panes have selections in order', async () => {
|
|
281
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
282
|
-
await tick();
|
|
283
|
-
let detail = null;
|
|
284
|
-
el.addEventListener('change', (e) => { detail = e.detail; });
|
|
285
|
-
const calFrom = el.querySelector('calendar-grid-ui[data-cal-from]');
|
|
286
|
-
const calTo = el.querySelector('calendar-grid-ui[data-cal-to]');
|
|
287
|
-
calFrom.value = '2026-03-01';
|
|
288
|
-
calFrom.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '2026-03-01' } }));
|
|
289
|
-
await tick();
|
|
290
|
-
calTo.value = '2026-03-15';
|
|
291
|
-
calTo.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '2026-03-15' } }));
|
|
292
|
-
await tick();
|
|
293
|
-
expect(detail).not.toBeNull();
|
|
294
|
-
expect(detail.value).toEqual({ from: '2026-03-01', to: '2026-03-15' });
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it('reversed range fires `invalid`, not `change`', async () => {
|
|
298
|
-
const el = mount('<date-range-picker-ui></date-range-picker-ui>');
|
|
299
|
-
await tick();
|
|
300
|
-
let invalidDetail = null;
|
|
301
|
-
let changeFired = false;
|
|
302
|
-
el.addEventListener('invalid', (e) => { invalidDetail = e.detail; });
|
|
303
|
-
el.addEventListener('change', () => { changeFired = true; });
|
|
304
|
-
const calFrom = el.querySelector('calendar-grid-ui[data-cal-from]');
|
|
305
|
-
const calTo = el.querySelector('calendar-grid-ui[data-cal-to]');
|
|
306
|
-
// Click `to` first (with a date), then `from` AFTER it (reversed).
|
|
307
|
-
calTo.value = '2026-03-01';
|
|
308
|
-
calTo.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '2026-03-01' } }));
|
|
309
|
-
await tick();
|
|
310
|
-
calFrom.value = '2026-03-15';
|
|
311
|
-
calFrom.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '2026-03-15' } }));
|
|
312
|
-
await tick();
|
|
313
|
-
expect(invalidDetail).not.toBeNull();
|
|
314
|
-
expect(invalidDetail.reason).toBe('reversed');
|
|
315
|
-
expect(changeFired).toBe(false);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it('clear() resets the value', async () => {
|
|
319
|
-
const el = mount('<date-range-picker-ui value=\'{"from":"2026-01-01","to":"2026-01-07"}\'></date-range-picker-ui>');
|
|
320
|
-
await tick();
|
|
321
|
-
el.clear();
|
|
322
|
-
await tick();
|
|
323
|
-
expect(el.value).toBe('');
|
|
324
|
-
expect(el.rangeValue).toBeNull();
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
describe('date-range-picker-ui — comparison mode', () => {
|
|
329
|
-
beforeEach(() => { document.body.innerHTML = ''; });
|
|
330
|
-
|
|
331
|
-
it('adds a comparison preset cluster when [comparison] is set', async () => {
|
|
332
|
-
const el = mount('<date-range-picker-ui comparison></date-range-picker-ui>');
|
|
333
|
-
await tick();
|
|
334
|
-
const rail = el.querySelector(':scope > [slot="popover"] > [data-preset-rail]');
|
|
335
|
-
const cmpButtons = rail.querySelectorAll('button-ui[data-comparison-preset-label]');
|
|
336
|
-
expect(cmpButtons.length).toBe(3);
|
|
337
|
-
expect(cmpButtons[0].dataset.comparisonPresetLabel).toBe('Previous period');
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it('parses compare-value into rangeCompareValue', async () => {
|
|
341
|
-
const el = mount('<date-range-picker-ui comparison compare-value=\'{"from":"2026-04-01","to":"2026-04-30"}\'></date-range-picker-ui>');
|
|
342
|
-
await tick();
|
|
343
|
-
expect(el.rangeCompareValue).toEqual({ from: '2026-04-01', to: '2026-04-30' });
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it('change detail carries compareValue when comparison is set', async () => {
|
|
347
|
-
const el = mount('<date-range-picker-ui comparison compare-value=\'{"from":"2026-04-01","to":"2026-04-30"}\'></date-range-picker-ui>');
|
|
348
|
-
await tick();
|
|
349
|
-
let detail = null;
|
|
350
|
-
el.addEventListener('change', (e) => { detail = e.detail; });
|
|
351
|
-
const calFrom = el.querySelector('calendar-grid-ui[data-cal-from]');
|
|
352
|
-
const calTo = el.querySelector('calendar-grid-ui[data-cal-to]');
|
|
353
|
-
calFrom.value = '2026-05-01';
|
|
354
|
-
calFrom.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '2026-05-01' } }));
|
|
355
|
-
await tick();
|
|
356
|
-
calTo.value = '2026-05-31';
|
|
357
|
-
calTo.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '2026-05-31' } }));
|
|
358
|
-
await tick();
|
|
359
|
-
expect(detail).not.toBeNull();
|
|
360
|
-
expect(detail.value).toEqual({ from: '2026-05-01', to: '2026-05-31' });
|
|
361
|
-
expect(detail.compareValue).toEqual({ from: '2026-04-01', to: '2026-04-30' });
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
describe('date-range-picker-ui — form participation', () => {
|
|
366
|
-
beforeEach(() => { document.body.innerHTML = ''; });
|
|
367
|
-
|
|
368
|
-
it('is form-associated', () => {
|
|
369
|
-
expect(customElements.get('date-range-picker-ui').formAssociated).toBe(true);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it('exposes ElementInternals via the host', async () => {
|
|
373
|
-
const el = mount('<date-range-picker-ui name="period"></date-range-picker-ui>');
|
|
374
|
-
await tick();
|
|
375
|
-
expect(el.internals).toBeDefined();
|
|
376
|
-
expect(typeof el.internals.setFormValue).toBe('function');
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('runs constraint validation on a reversed range (post-commit attempt is rejected)', async () => {
|
|
380
|
-
const el = mount('<date-range-picker-ui name="period" value=\'{"from":"2026-01-01","to":"2026-01-07"}\'></date-range-picker-ui>');
|
|
381
|
-
await tick();
|
|
382
|
-
// Apply a reversed range programmatically via direct value write,
|
|
383
|
-
// then sync — happy-dom's setValidity is a no-op, so we just confirm
|
|
384
|
-
// syncValue doesn't throw.
|
|
385
|
-
expect(() => el.syncValue('{"from":"2026-12-31","to":"2026-01-01"}')).not.toThrow();
|
|
386
|
-
});
|
|
387
|
-
});
|