@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,20 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { noiseTexture } from './noise-texture.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('noise-texture', () => {
|
|
6
|
-
beforeEach(resetDOM);
|
|
7
|
-
|
|
8
|
-
it('connect sets data-noise-texture-active and appends an overlay', () => {
|
|
9
|
-
const host = mountHost();
|
|
10
|
-
connectTrait(noiseTexture, host);
|
|
11
|
-
expect(host.hasAttribute('data-noise-texture-active')).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('disconnect clears active + removes overlay', () => {
|
|
15
|
-
const host = mountHost();
|
|
16
|
-
const inst = connectTrait(noiseTexture, host);
|
|
17
|
-
inst.disconnect(host);
|
|
18
|
-
expect(host.hasAttribute('data-noise-texture-active')).toBe(false);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { parallax } from './parallax.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('parallax', () => {
|
|
6
|
-
beforeEach(resetDOM);
|
|
7
|
-
|
|
8
|
-
it('connect sets data-parallax-active', () => {
|
|
9
|
-
const host = mountHost();
|
|
10
|
-
connectTrait(parallax, host);
|
|
11
|
-
expect(host.hasAttribute('data-parallax-active')).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('disconnect clears active + translate', () => {
|
|
15
|
-
const host = mountHost();
|
|
16
|
-
const inst = connectTrait(parallax, host);
|
|
17
|
-
inst.disconnect(host);
|
|
18
|
-
expect(host.hasAttribute('data-parallax-active')).toBe(false);
|
|
19
|
-
expect(host.style.translate).toBe('');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('respects data-parallax-strength (default 0.5)', () => {
|
|
23
|
-
const host = mountHost('div', { 'data-parallax-strength': '0.2' });
|
|
24
|
-
expect(() => connectTrait(parallax, host)).not.toThrow();
|
|
25
|
-
});
|
|
26
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { portal } from './portal.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('portal', () => {
|
|
6
|
-
beforeEach(resetDOM);
|
|
7
|
-
|
|
8
|
-
it('connect sets data-portal-active', () => {
|
|
9
|
-
const host = mountHost();
|
|
10
|
-
connectTrait(portal, host);
|
|
11
|
-
expect(host.hasAttribute('data-portal-active')).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('disconnect clears the active attribute', () => {
|
|
15
|
-
const host = mountHost();
|
|
16
|
-
const inst = connectTrait(portal, host);
|
|
17
|
-
inst.disconnect(host);
|
|
18
|
-
expect(host.hasAttribute('data-portal-active')).toBe(false);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('with data-portal-target, host is moved to the target element', () => {
|
|
22
|
-
const target = document.createElement('div');
|
|
23
|
-
target.id = 'portal-out';
|
|
24
|
-
document.body.appendChild(target);
|
|
25
|
-
const host = mountHost('div', { 'data-portal-target': '#portal-out' });
|
|
26
|
-
connectTrait(portal, host);
|
|
27
|
-
// The trait moves the host into the target on connect.
|
|
28
|
-
expect(target.contains(host)).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { pressable } from './pressable.js';
|
|
3
|
-
import { mountHost, connectTrait, spyEvent, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('pressable', () => {
|
|
6
|
-
beforeEach(resetDOM);
|
|
7
|
-
|
|
8
|
-
it('sets data-pressable-pressed on pointerdown, removes on pointerup', () => {
|
|
9
|
-
const host = mountHost();
|
|
10
|
-
connectTrait(pressable, host);
|
|
11
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
12
|
-
expect(host.hasAttribute('data-pressable-pressed')).toBe(true);
|
|
13
|
-
host.dispatchEvent(new PointerEvent('pointerup'));
|
|
14
|
-
expect(host.hasAttribute('data-pressable-pressed')).toBe(false);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('dispatches "press" event on pointerup', () => {
|
|
18
|
-
const host = mountHost();
|
|
19
|
-
connectTrait(pressable, host);
|
|
20
|
-
const spy = spyEvent(host, 'press');
|
|
21
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
22
|
-
host.dispatchEvent(new PointerEvent('pointerup'));
|
|
23
|
-
expect(spy.count).toBe(1);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('dispatches "press" on Enter/Space keyup', () => {
|
|
27
|
-
const host = mountHost();
|
|
28
|
-
connectTrait(pressable, host);
|
|
29
|
-
const spy = spyEvent(host, 'press');
|
|
30
|
-
host.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
|
31
|
-
host.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
|
|
32
|
-
expect(spy.count).toBe(1);
|
|
33
|
-
host.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
|
34
|
-
host.dispatchEvent(new KeyboardEvent('keyup', { key: ' ' }));
|
|
35
|
-
expect(spy.count).toBe(2);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('respects [disabled] — no event, no attribute toggle', () => {
|
|
39
|
-
const host = mountHost('button', { disabled: '' });
|
|
40
|
-
connectTrait(pressable, host);
|
|
41
|
-
const spy = spyEvent(host, 'press');
|
|
42
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
43
|
-
expect(host.hasAttribute('data-pressable-pressed')).toBe(false);
|
|
44
|
-
host.dispatchEvent(new PointerEvent('pointerup'));
|
|
45
|
-
expect(spy.count).toBe(0);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('clears pressed state on pointerleave (canceled press)', () => {
|
|
49
|
-
const host = mountHost();
|
|
50
|
-
connectTrait(pressable, host);
|
|
51
|
-
const spy = spyEvent(host, 'press');
|
|
52
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
53
|
-
expect(host.hasAttribute('data-pressable-pressed')).toBe(true);
|
|
54
|
-
host.dispatchEvent(new PointerEvent('pointerleave'));
|
|
55
|
-
expect(host.hasAttribute('data-pressable-pressed')).toBe(false);
|
|
56
|
-
host.dispatchEvent(new PointerEvent('pointerup'));
|
|
57
|
-
expect(spy.count).toBe(0);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('disconnect removes all listeners and clears attribute', () => {
|
|
61
|
-
const host = mountHost();
|
|
62
|
-
const inst = connectTrait(pressable, host);
|
|
63
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
64
|
-
expect(host.hasAttribute('data-pressable-pressed')).toBe(true);
|
|
65
|
-
inst.disconnect(host);
|
|
66
|
-
expect(host.hasAttribute('data-pressable-pressed')).toBe(false);
|
|
67
|
-
// Re-firing events post-disconnect is a no-op
|
|
68
|
-
const spy = spyEvent(host, 'press');
|
|
69
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
70
|
-
host.dispatchEvent(new PointerEvent('pointerup'));
|
|
71
|
-
expect(spy.count).toBe(0);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { resettable } from './resettable.js';
|
|
3
|
-
import { mountHost, connectTrait, spyEvent, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
function mountInForm(initialValue = 'initial') {
|
|
6
|
-
const form = document.createElement('form');
|
|
7
|
-
document.body.appendChild(form);
|
|
8
|
-
const host = document.createElement('input');
|
|
9
|
-
host.value = initialValue;
|
|
10
|
-
form.appendChild(host);
|
|
11
|
-
return { form, host };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe('resettable', () => {
|
|
15
|
-
beforeEach(resetDOM);
|
|
16
|
-
|
|
17
|
-
it('captures initial value on connect; restores it on form reset', () => {
|
|
18
|
-
const { form, host } = mountInForm('original');
|
|
19
|
-
connectTrait(resettable, host);
|
|
20
|
-
host.value = 'modified';
|
|
21
|
-
form.dispatchEvent(new Event('reset', { bubbles: true }));
|
|
22
|
-
expect(host.value).toBe('original');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('dispatches reset-applied with the initial value in detail', () => {
|
|
26
|
-
const { form, host } = mountInForm('hello');
|
|
27
|
-
connectTrait(resettable, host);
|
|
28
|
-
const spy = spyEvent(host, 'reset-applied');
|
|
29
|
-
host.value = 'changed';
|
|
30
|
-
form.dispatchEvent(new Event('reset', { bubbles: true }));
|
|
31
|
-
expect(spy.count).toBe(1);
|
|
32
|
-
expect(spy.last.initialValue).toBe('hello');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('outside a form: no-op (no throw, no listener)', () => {
|
|
36
|
-
const host = mountHost('input', { value: 'x' });
|
|
37
|
-
expect(() => connectTrait(resettable, host)).not.toThrow();
|
|
38
|
-
expect(host.hasAttribute('data-resettable-active')).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('disconnect removes the form listener', () => {
|
|
42
|
-
const { form, host } = mountInForm('a');
|
|
43
|
-
const inst = resettable();
|
|
44
|
-
inst.connect(host);
|
|
45
|
-
inst.disconnect(host);
|
|
46
|
-
host.value = 'b';
|
|
47
|
-
form.dispatchEvent(new Event('reset', { bubbles: true }));
|
|
48
|
-
// After disconnect, the trait should NOT have reset the value.
|
|
49
|
-
expect(host.value).toBe('b');
|
|
50
|
-
expect(host.hasAttribute('data-resettable-active')).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('multiple controls in the same form each reset independently', () => {
|
|
54
|
-
const form = document.createElement('form');
|
|
55
|
-
document.body.appendChild(form);
|
|
56
|
-
const a = document.createElement('input'); a.value = 'A';
|
|
57
|
-
const b = document.createElement('input'); b.value = 'B';
|
|
58
|
-
form.appendChild(a); form.appendChild(b);
|
|
59
|
-
resettable().connect(a);
|
|
60
|
-
resettable().connect(b);
|
|
61
|
-
a.value = 'changed-a';
|
|
62
|
-
b.value = 'changed-b';
|
|
63
|
-
form.dispatchEvent(new Event('reset', { bubbles: true }));
|
|
64
|
-
expect(a.value).toBe('A');
|
|
65
|
-
expect(b.value).toBe('B');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('boolean controls (host.checked) restore their initial checked state', () => {
|
|
69
|
-
// Synthesize a switch-like host: the only state is a boolean `checked`
|
|
70
|
-
// reactive property — no `value`. Mirrors switch-ui's API.
|
|
71
|
-
const form = document.createElement('form');
|
|
72
|
-
document.body.appendChild(form);
|
|
73
|
-
const host = document.createElement('div');
|
|
74
|
-
host.checked = true;
|
|
75
|
-
form.appendChild(host);
|
|
76
|
-
|
|
77
|
-
connectTrait(resettable, host);
|
|
78
|
-
const spy = spyEvent(host, 'reset-applied');
|
|
79
|
-
|
|
80
|
-
host.checked = false; // user toggles it off
|
|
81
|
-
form.dispatchEvent(new Event('reset', { bubbles: true }));
|
|
82
|
-
|
|
83
|
-
expect(host.checked).toBe(true);
|
|
84
|
-
expect(spy.count).toBe(1);
|
|
85
|
-
expect(spy.last.initialValue).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('boolean controls starting unchecked stay unchecked after reset', () => {
|
|
89
|
-
const form = document.createElement('form');
|
|
90
|
-
document.body.appendChild(form);
|
|
91
|
-
const host = document.createElement('div');
|
|
92
|
-
host.checked = false;
|
|
93
|
-
form.appendChild(host);
|
|
94
|
-
|
|
95
|
-
connectTrait(resettable, host);
|
|
96
|
-
host.checked = true;
|
|
97
|
-
form.dispatchEvent(new Event('reset', { bubbles: true }));
|
|
98
|
-
expect(host.checked).toBe(false);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { resizable } from './resizable.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('resizable', () => {
|
|
6
|
-
beforeEach(resetDOM);
|
|
7
|
-
|
|
8
|
-
it('connect attaches without throwing', () => {
|
|
9
|
-
const host = mountHost();
|
|
10
|
-
expect(() => connectTrait(resizable, host)).not.toThrow();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('disconnect clears resizing state attributes', () => {
|
|
14
|
-
const host = mountHost();
|
|
15
|
-
const inst = connectTrait(resizable, host);
|
|
16
|
-
expect(() => inst.disconnect(host)).not.toThrow();
|
|
17
|
-
expect(host.hasAttribute('data-resizable-resizing')).toBe(false);
|
|
18
|
-
expect(host.hasAttribute('data-resizable-edge')).toBe(false);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('creates 8 hit-zones — 4 edges + 4 corners (resize in x, y, and x/y)', () => {
|
|
22
|
-
const host = mountHost();
|
|
23
|
-
connectTrait(resizable, host);
|
|
24
|
-
const names = [...host.querySelectorAll('[data-resizable-handle]')]
|
|
25
|
-
.map((el) => el.dataset.resizableHandle);
|
|
26
|
-
expect(names).toHaveLength(8);
|
|
27
|
-
expect(names).toEqual(expect.arrayContaining([
|
|
28
|
-
'top', 'right', 'bottom', 'left',
|
|
29
|
-
'top-left', 'top-right', 'bottom-right', 'bottom-left',
|
|
30
|
-
]));
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('disconnect removes all hit-zones', () => {
|
|
34
|
-
const host = mountHost();
|
|
35
|
-
const inst = connectTrait(resizable, host);
|
|
36
|
-
inst.disconnect(host);
|
|
37
|
-
expect(host.querySelectorAll('[data-resizable-handle]')).toHaveLength(0);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
2
|
-
import { resizeObserver } from './resize-observer.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('resize-observer', () => {
|
|
6
|
-
let originalRO;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
resetDOM();
|
|
10
|
-
originalRO = globalThis.ResizeObserver;
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (originalRO) globalThis.ResizeObserver = originalRO;
|
|
15
|
-
else delete globalThis.ResizeObserver;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('without ResizeObserver: gracefully no-ops on connect', () => {
|
|
19
|
-
delete globalThis.ResizeObserver;
|
|
20
|
-
const host = mountHost();
|
|
21
|
-
expect(() => connectTrait(resizeObserver, host)).not.toThrow();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('with mocked ResizeObserver: observe() called + disconnect cleans up', () => {
|
|
25
|
-
const observe = vi.fn();
|
|
26
|
-
const roDisconnect = vi.fn();
|
|
27
|
-
globalThis.ResizeObserver = function MockRO() {
|
|
28
|
-
this.observe = observe;
|
|
29
|
-
this.disconnect = roDisconnect;
|
|
30
|
-
this.unobserve = vi.fn();
|
|
31
|
-
};
|
|
32
|
-
const host = mountHost();
|
|
33
|
-
const inst = connectTrait(resizeObserver, host);
|
|
34
|
-
expect(observe).toHaveBeenCalledWith(host);
|
|
35
|
-
inst.disconnect(host);
|
|
36
|
-
expect(roDisconnect).toHaveBeenCalled();
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { ripple } from './ripple.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('ripple', () => {
|
|
6
|
-
beforeEach(resetDOM);
|
|
7
|
-
|
|
8
|
-
it('connect sets data-ripple-active', () => {
|
|
9
|
-
const host = mountHost();
|
|
10
|
-
connectTrait(ripple, host);
|
|
11
|
-
expect(host.hasAttribute('data-ripple-active')).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('disconnect clears active + restores overflow', () => {
|
|
15
|
-
const host = mountHost();
|
|
16
|
-
const inst = connectTrait(ripple, host);
|
|
17
|
-
inst.disconnect(host);
|
|
18
|
-
expect(host.hasAttribute('data-ripple-active')).toBe(false);
|
|
19
|
-
expect(host.style.overflow).toBe('');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('click appends a ripple span (in non-reduced-motion environment)', () => {
|
|
23
|
-
const host = mountHost();
|
|
24
|
-
connectTrait(ripple, host);
|
|
25
|
-
const beforeChildren = host.children.length;
|
|
26
|
-
host.dispatchEvent(new MouseEvent('click', { clientX: 10, clientY: 10, bubbles: true }));
|
|
27
|
-
// In happy-dom + reduced-motion preference unknown, the ripple may or may
|
|
28
|
-
// not append a span. The contract is "connect doesn't throw, no DOM leak
|
|
29
|
-
// on disconnect".
|
|
30
|
-
expect(host.children.length).toBeGreaterThanOrEqual(beforeChildren);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { rovingTabindex } from './roving-tabindex.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
function tabbableChild(host) {
|
|
6
|
-
const btn = document.createElement('button');
|
|
7
|
-
host.appendChild(btn);
|
|
8
|
-
return btn;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe('roving-tabindex', () => {
|
|
12
|
-
beforeEach(resetDOM);
|
|
13
|
-
|
|
14
|
-
it('connect attaches without throwing', () => {
|
|
15
|
-
const host = mountHost();
|
|
16
|
-
tabbableChild(host);
|
|
17
|
-
tabbableChild(host);
|
|
18
|
-
tabbableChild(host);
|
|
19
|
-
expect(() => connectTrait(rovingTabindex, host)).not.toThrow();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('disconnect cleans up', () => {
|
|
23
|
-
const host = mountHost();
|
|
24
|
-
tabbableChild(host);
|
|
25
|
-
const inst = connectTrait(rovingTabindex, host);
|
|
26
|
-
expect(() => inst.disconnect(host)).not.toThrow();
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { scalePress } from './scale-press.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('scale-press', () => {
|
|
6
|
-
beforeEach(resetDOM);
|
|
7
|
-
|
|
8
|
-
it('pointerdown applies scale(0.95) + active attribute', () => {
|
|
9
|
-
const host = mountHost();
|
|
10
|
-
connectTrait(scalePress, host);
|
|
11
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
12
|
-
expect(host.hasAttribute('data-scale-press-active')).toBe(true);
|
|
13
|
-
expect(host.style.transform).toContain('0.95');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('pointerup releases scale and clears attribute', () => {
|
|
17
|
-
const host = mountHost();
|
|
18
|
-
connectTrait(scalePress, host);
|
|
19
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
20
|
-
host.dispatchEvent(new PointerEvent('pointerup'));
|
|
21
|
-
expect(host.hasAttribute('data-scale-press-active')).toBe(false);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('pointerleave during press releases (canceled press)', () => {
|
|
25
|
-
const host = mountHost();
|
|
26
|
-
connectTrait(scalePress, host);
|
|
27
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
28
|
-
host.dispatchEvent(new PointerEvent('pointerleave'));
|
|
29
|
-
expect(host.hasAttribute('data-scale-press-active')).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('respects [disabled] — no transform, no attribute', () => {
|
|
33
|
-
const host = mountHost('button', { disabled: '' });
|
|
34
|
-
connectTrait(scalePress, host);
|
|
35
|
-
host.dispatchEvent(new PointerEvent('pointerdown'));
|
|
36
|
-
expect(host.hasAttribute('data-scale-press-active')).toBe(false);
|
|
37
|
-
expect(host.style.transform).toBe('');
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { scrollLock } from './scroll-lock.js';
|
|
3
|
-
import { mountHost, connectTrait, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('scroll-lock', () => {
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
resetDOM();
|
|
8
|
-
document.body.style.overflow = '';
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('connect sets body overflow:hidden + active attribute', () => {
|
|
12
|
-
const host = mountHost();
|
|
13
|
-
const inst = connectTrait(scrollLock, host);
|
|
14
|
-
expect(document.body.style.overflow).toBe('hidden');
|
|
15
|
-
expect(host.hasAttribute('data-scroll-lock-active')).toBe(true);
|
|
16
|
-
// Balanced disconnect to keep the module-level lockCount honest for sibling tests.
|
|
17
|
-
inst.disconnect(host);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('disconnect restores body overflow + clears attribute', () => {
|
|
21
|
-
document.body.style.overflow = 'auto';
|
|
22
|
-
const host = mountHost();
|
|
23
|
-
const inst = connectTrait(scrollLock, host);
|
|
24
|
-
inst.disconnect(host);
|
|
25
|
-
expect(document.body.style.overflow).toBe('auto');
|
|
26
|
-
expect(host.hasAttribute('data-scroll-lock-active')).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('refcounted — disconnect order does not unlock until last consumer leaves', () => {
|
|
30
|
-
// The trait is module-scoped (lockCount + savedOverflow live in module state).
|
|
31
|
-
// Snapshot the current state so this assertion is independent of test order.
|
|
32
|
-
const startOverflow = document.body.style.overflow;
|
|
33
|
-
const a = mountHost();
|
|
34
|
-
const b = mountHost();
|
|
35
|
-
const instA = connectTrait(scrollLock, a);
|
|
36
|
-
const instB = connectTrait(scrollLock, b);
|
|
37
|
-
expect(document.body.style.overflow).toBe('hidden');
|
|
38
|
-
instA.disconnect(a);
|
|
39
|
-
// Still locked because b is still active
|
|
40
|
-
expect(document.body.style.overflow).toBe('hidden');
|
|
41
|
-
instB.disconnect(b);
|
|
42
|
-
// Restored to whatever it was when the *first* lock acquired it
|
|
43
|
-
expect(document.body.style.overflow).toBe(startOverflow);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
2
|
-
import { scrollProgress } from './scroll-progress.js';
|
|
3
|
-
import { mountHost, connectTrait, spyEvent, resetDOM } from '../test-helpers.js';
|
|
4
|
-
|
|
5
|
-
describe('scroll-progress', () => {
|
|
6
|
-
let originalRAF;
|
|
7
|
-
let originalCAF;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
resetDOM();
|
|
11
|
-
// Synchronous rAF — the trait's scheduler calls back immediately so
|
|
12
|
-
// we can assert post-update side-effects without awaiting frames.
|
|
13
|
-
originalRAF = globalThis.requestAnimationFrame;
|
|
14
|
-
originalCAF = globalThis.cancelAnimationFrame;
|
|
15
|
-
globalThis.requestAnimationFrame = (fn) => { fn(performance.now?.() ?? 0); return 1; };
|
|
16
|
-
globalThis.cancelAnimationFrame = vi.fn();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
globalThis.requestAnimationFrame = originalRAF;
|
|
21
|
-
globalThis.cancelAnimationFrame = originalCAF;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('connect sets data-scroll-progress-active', () => {
|
|
25
|
-
const host = mountHost();
|
|
26
|
-
connectTrait(scrollProgress, host);
|
|
27
|
-
expect(host.hasAttribute('data-scroll-progress-active')).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('disconnect clears active + progress + CSS variable', () => {
|
|
31
|
-
const host = mountHost();
|
|
32
|
-
const inst = connectTrait(scrollProgress, host);
|
|
33
|
-
inst.disconnect(host);
|
|
34
|
-
expect(host.hasAttribute('data-scroll-progress-active')).toBe(false);
|
|
35
|
-
expect(host.hasAttribute('data-scroll-progress')).toBe(false);
|
|
36
|
-
expect(host.style.getPropertyValue('--scroll-progress')).toBe('');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('writes data-scroll-progress as a 0..1 string after connect', () => {
|
|
40
|
-
const host = mountHost();
|
|
41
|
-
connectTrait(scrollProgress, host);
|
|
42
|
-
// Synchronous rAF means the seed update has already run.
|
|
43
|
-
const v = host.getAttribute('data-scroll-progress');
|
|
44
|
-
expect(v).not.toBeNull();
|
|
45
|
-
const n = parseFloat(v);
|
|
46
|
-
expect(n).toBeGreaterThanOrEqual(0);
|
|
47
|
-
expect(n).toBeLessThanOrEqual(1);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('writes the --scroll-progress CSS custom property', () => {
|
|
51
|
-
const host = mountHost();
|
|
52
|
-
connectTrait(scrollProgress, host);
|
|
53
|
-
const css = host.style.getPropertyValue('--scroll-progress');
|
|
54
|
-
expect(css).not.toBe('');
|
|
55
|
-
const n = parseFloat(css);
|
|
56
|
-
expect(n).toBeGreaterThanOrEqual(0);
|
|
57
|
-
expect(n).toBeLessThanOrEqual(1);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('dispatches a scroll-progress event with { progress } detail', () => {
|
|
61
|
-
const host = mountHost();
|
|
62
|
-
const spy = spyEvent(host, 'scroll-progress');
|
|
63
|
-
connectTrait(scrollProgress, host);
|
|
64
|
-
// Initial seed fires once via synchronous rAF.
|
|
65
|
-
expect(spy.count).toBeGreaterThanOrEqual(1);
|
|
66
|
-
expect(typeof spy.last.progress).toBe('number');
|
|
67
|
-
expect(spy.last.progress).toBeGreaterThanOrEqual(0);
|
|
68
|
-
expect(spy.last.progress).toBeLessThanOrEqual(1);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('mode="page" falls back to 0 when scroll range is zero', () => {
|
|
72
|
-
const host = mountHost('div', { 'data-scroll-progress-mode': 'page' });
|
|
73
|
-
connectTrait(scrollProgress, host);
|
|
74
|
-
// happy-dom reports zero scroll geometry by default.
|
|
75
|
-
expect(host.getAttribute('data-scroll-progress')).toBe('0.000');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('mode="scrolled" reports 0..1 from host scrollTop / max', () => {
|
|
79
|
-
const host = mountHost('div', { 'data-scroll-progress-mode': 'scrolled' });
|
|
80
|
-
// Stub the geometry — happy-dom returns zeros for scroll metrics.
|
|
81
|
-
Object.defineProperty(host, 'scrollHeight', { configurable: true, value: 1000 });
|
|
82
|
-
Object.defineProperty(host, 'clientHeight', { configurable: true, value: 200 });
|
|
83
|
-
Object.defineProperty(host, 'scrollTop', { configurable: true, value: 200, writable: true });
|
|
84
|
-
connectTrait(scrollProgress, host);
|
|
85
|
-
const v = parseFloat(host.getAttribute('data-scroll-progress'));
|
|
86
|
-
// 200 / (1000 - 200) = 0.25
|
|
87
|
-
expect(v).toBeCloseTo(0.25, 2);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('mode="in-view" clamps to [0,1] for offscreen rects', () => {
|
|
91
|
-
const host = mountHost();
|
|
92
|
-
// Far below the viewport.
|
|
93
|
-
host.getBoundingClientRect = () => ({ top: 99999, bottom: 100099, left: 0, right: 0, width: 0, height: 100, x: 0, y: 99999, toJSON() { return {}; } });
|
|
94
|
-
connectTrait(scrollProgress, host);
|
|
95
|
-
expect(parseFloat(host.getAttribute('data-scroll-progress'))).toBe(0);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('mode="in-view" reports 1 for rects fully scrolled past', () => {
|
|
99
|
-
const host = mountHost();
|
|
100
|
-
// Far above the viewport.
|
|
101
|
-
host.getBoundingClientRect = () => ({ top: -2000, bottom: -1900, left: 0, right: 0, width: 0, height: 100, x: 0, y: -2000, toJSON() { return {}; } });
|
|
102
|
-
// Make sure window.innerHeight is sane.
|
|
103
|
-
Object.defineProperty(window, 'innerHeight', { configurable: true, value: 800 });
|
|
104
|
-
connectTrait(scrollProgress, host);
|
|
105
|
-
expect(parseFloat(host.getAttribute('data-scroll-progress'))).toBe(1);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('data-scroll-container selector resolves an explicit ancestor', () => {
|
|
109
|
-
document.body.innerHTML = '<div id="custom-scroller" style="overflow:auto;"><div id="inner"></div></div>';
|
|
110
|
-
const host = document.getElementById('inner');
|
|
111
|
-
host.setAttribute('data-scroll-container', '#custom-scroller');
|
|
112
|
-
const scroller = document.getElementById('custom-scroller');
|
|
113
|
-
const addSpy = vi.spyOn(scroller, 'addEventListener');
|
|
114
|
-
connectTrait(scrollProgress, host);
|
|
115
|
-
// The trait should attach a scroll listener to the resolved container.
|
|
116
|
-
const calls = addSpy.mock.calls.filter(([type]) => type === 'scroll');
|
|
117
|
-
expect(calls.length).toBeGreaterThanOrEqual(1);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('invalid data-scroll-container selector falls through to ancestor walk', () => {
|
|
121
|
-
document.body.innerHTML = '<div id="parent" style="overflow:auto;"><div id="child"></div></div>';
|
|
122
|
-
const host = document.getElementById('child');
|
|
123
|
-
host.setAttribute('data-scroll-container', '###not-a-valid-selector');
|
|
124
|
-
expect(() => connectTrait(scrollProgress, host)).not.toThrow();
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('schedules through rAF — second scroll within the same frame coalesces', () => {
|
|
128
|
-
let queued = 0;
|
|
129
|
-
let pending = null;
|
|
130
|
-
globalThis.requestAnimationFrame = (fn) => {
|
|
131
|
-
queued++;
|
|
132
|
-
pending = fn;
|
|
133
|
-
return queued;
|
|
134
|
-
};
|
|
135
|
-
globalThis.cancelAnimationFrame = vi.fn();
|
|
136
|
-
const host = mountHost();
|
|
137
|
-
const inst = connectTrait(scrollProgress, host);
|
|
138
|
-
const baseline = queued;
|
|
139
|
-
// Fire two scroll events back-to-back without flushing the rAF.
|
|
140
|
-
window.dispatchEvent(new Event('scroll'));
|
|
141
|
-
window.dispatchEvent(new Event('scroll'));
|
|
142
|
-
// Only one rAF should be queued in addition to the seed.
|
|
143
|
-
expect(queued - baseline).toBeLessThanOrEqual(1);
|
|
144
|
-
// Flush manually so disconnect has nothing pending.
|
|
145
|
-
if (pending) pending(0);
|
|
146
|
-
inst.disconnect(host);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('disconnect cancels the pending rAF', () => {
|
|
150
|
-
let pendingId = null;
|
|
151
|
-
globalThis.requestAnimationFrame = () => { pendingId = 42; return 42; };
|
|
152
|
-
const cancelSpy = vi.fn();
|
|
153
|
-
globalThis.cancelAnimationFrame = cancelSpy;
|
|
154
|
-
const host = mountHost();
|
|
155
|
-
const inst = connectTrait(scrollProgress, host);
|
|
156
|
-
inst.disconnect(host);
|
|
157
|
-
// The trait queued one rAF on connect; disconnect should cancel it.
|
|
158
|
-
expect(cancelSpy).toHaveBeenCalled();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('disconnect removes the scroll listener from the resolved container', () => {
|
|
162
|
-
document.body.innerHTML = '<div id="scr" style="overflow:auto;"><div id="h"></div></div>';
|
|
163
|
-
const host = document.getElementById('h');
|
|
164
|
-
host.setAttribute('data-scroll-container', '#scr');
|
|
165
|
-
const scroller = document.getElementById('scr');
|
|
166
|
-
const removeSpy = vi.spyOn(scroller, 'removeEventListener');
|
|
167
|
-
const inst = connectTrait(scrollProgress, host);
|
|
168
|
-
inst.disconnect(host);
|
|
169
|
-
const calls = removeSpy.mock.calls.filter(([type]) => type === 'scroll');
|
|
170
|
-
expect(calls.length).toBeGreaterThanOrEqual(1);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('reconnect after disconnect does not throw and restores the active flag', () => {
|
|
174
|
-
const host = mountHost();
|
|
175
|
-
const inst1 = connectTrait(scrollProgress, host);
|
|
176
|
-
inst1.disconnect(host);
|
|
177
|
-
const inst2 = connectTrait(scrollProgress, host);
|
|
178
|
-
expect(host.hasAttribute('data-scroll-progress-active')).toBe(true);
|
|
179
|
-
inst2.disconnect(host);
|
|
180
|
-
expect(host.hasAttribute('data-scroll-progress-active')).toBe(false);
|
|
181
|
-
});
|
|
182
|
-
});
|