@spectral_labs/ui 1.6.1 → 1.8.0-next.0
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/LICENSE +21 -0
- package/THIRD_PARTY_NOTICES.md +201 -0
- package/dist/components/command/command.svelte.d.ts +30 -0
- package/dist/components/command/command.svelte.d.ts.map +1 -1
- package/dist/components/command/command.svelte.js +85 -4
- package/dist/components/command/components/command-group.svelte +25 -15
- package/dist/components/command/components/command-group.svelte.d.ts.map +1 -1
- package/dist/components/command/components/command-input.svelte +54 -5
- package/dist/components/command/components/command-input.svelte.d.ts +1 -1
- package/dist/components/command/components/command-input.svelte.d.ts.map +1 -1
- package/dist/components/command/components/command-list.svelte +22 -3
- package/dist/components/command/components/command-list.svelte.d.ts.map +1 -1
- package/dist/components/command/components/command-root.svelte +43 -4
- package/dist/components/command/components/command-root.svelte.d.ts.map +1 -1
- package/dist/components/command/index.d.ts +1 -0
- package/dist/components/command/index.d.ts.map +1 -1
- package/dist/components/command/types.d.ts +59 -4
- package/dist/components/command/types.d.ts.map +1 -1
- package/package.json +8 -2
- package/dist/components/__tests__/overlay-scrim-source.test.js +0 -134
- package/dist/components/accordion/__tests__/AccordionTest.svelte +0 -31
- package/dist/components/accordion/__tests__/accordion.test.svelte.js +0 -139
- package/dist/components/alert-dialog/__tests__/AlertDialogTest.svelte +0 -28
- package/dist/components/alert-dialog/__tests__/alert-dialog.a11y.test.js +0 -42
- package/dist/components/alert-dialog/__tests__/alert-dialog.integration.test.svelte.js +0 -51
- package/dist/components/alert-dialog/__tests__/alert-dialog.test.svelte.js +0 -113
- package/dist/components/app-bar/__tests__/app-bar.test.svelte.js +0 -30
- package/dist/components/aspect-ratio/__tests__/aspect-ratio.test.svelte.js +0 -22
- package/dist/components/avatar/__tests__/avatar.test.svelte.js +0 -26
- package/dist/components/badge/__tests__/badge.test.svelte.js +0 -30
- package/dist/components/bottom-sheet/__tests__/BottomSheetTest.svelte +0 -22
- package/dist/components/bottom-sheet/__tests__/bottom-sheet.test.svelte.js +0 -83
- package/dist/components/button/__tests__/button.test.svelte.js +0 -63
- package/dist/components/calendar/__tests__/CalendarTest.svelte +0 -56
- package/dist/components/calendar/__tests__/calendar.test.svelte.js +0 -190
- package/dist/components/card/__tests__/card.test.svelte.js +0 -30
- package/dist/components/checkbox/__tests__/checkbox.test.svelte.js +0 -44
- package/dist/components/chips/__tests__/chips.test.svelte.js +0 -26
- package/dist/components/collapsible/__tests__/CollapsibleTest.svelte +0 -18
- package/dist/components/collapsible/__tests__/collapsible.test.svelte.js +0 -125
- package/dist/components/combobox/__tests__/ComboboxTest.svelte +0 -40
- package/dist/components/combobox/__tests__/combobox.test.svelte.js +0 -255
- package/dist/components/command/__tests__/CommandTest.svelte +0 -77
- package/dist/components/command/__tests__/command-item-passthrough.test.svelte.js +0 -284
- package/dist/components/command/__tests__/command.a11y.test.js +0 -30
- package/dist/components/command/__tests__/command.integration.test.svelte.js +0 -68
- package/dist/components/command/__tests__/command.test.svelte.js +0 -230
- package/dist/components/context-menu/__tests__/ContextMenuNonInteractiveTest.svelte +0 -32
- package/dist/components/context-menu/__tests__/ContextMenuTest.svelte +0 -25
- package/dist/components/context-menu/__tests__/context-menu.test.svelte.js +0 -141
- package/dist/components/date-field/__tests__/DateFieldTest.svelte +0 -20
- package/dist/components/date-field/__tests__/date-field.test.svelte.js +0 -139
- package/dist/components/date-picker/__tests__/DatePickerTest.svelte +0 -20
- package/dist/components/date-picker/__tests__/date-picker.test.svelte.js +0 -190
- package/dist/components/date-range-field/__tests__/DateRangeFieldTest.svelte +0 -20
- package/dist/components/date-range-field/__tests__/date-range-field.test.svelte.js +0 -130
- package/dist/components/date-range-picker/__tests__/DateRangePickerTest.svelte +0 -18
- package/dist/components/date-range-picker/__tests__/date-range-picker.test.svelte.js +0 -156
- package/dist/components/dialog/__tests__/DialogTest.svelte +0 -34
- package/dist/components/dialog/__tests__/dialog.test.svelte.js +0 -184
- package/dist/components/drop-zone/__tests__/drop-zone.test.svelte.js +0 -26
- package/dist/components/dropdown-menu/__tests__/DropdownMenuRadioTest.svelte +0 -90
- package/dist/components/dropdown-menu/__tests__/DropdownMenuTest.svelte +0 -71
- package/dist/components/dropdown-menu/__tests__/dropdown-menu-radio-group.test.svelte.js +0 -306
- package/dist/components/dropdown-menu/__tests__/dropdown-menu.test.svelte.js +0 -567
- package/dist/components/empty-state/__tests__/empty-state.test.svelte.js +0 -26
- package/dist/components/fab/__tests__/fab.test.svelte.js +0 -30
- package/dist/components/field/__tests__/FieldTest.svelte +0 -26
- package/dist/components/field/__tests__/field.test.svelte.js +0 -74
- package/dist/components/grid-list/__tests__/GridListTest.svelte +0 -46
- package/dist/components/grid-list/__tests__/grid-list.test.svelte.js +0 -231
- package/dist/components/hover-card/__tests__/HoverCardTest.svelte +0 -30
- package/dist/components/hover-card/__tests__/hover-card.test.svelte.js +0 -236
- package/dist/components/indicator/__tests__/indicator.test.svelte.js +0 -30
- package/dist/components/input/__tests__/input.test.svelte.js +0 -67
- package/dist/components/label/__tests__/label.test.svelte.js +0 -22
- package/dist/components/language-select/__tests__/language-select.test.svelte.js +0 -199
- package/dist/components/language-select/__tests__/languages.test.js +0 -87
- package/dist/components/link-preview/__tests__/LinkPreviewTest.svelte +0 -37
- package/dist/components/link-preview/__tests__/link-preview.a11y.test.js +0 -28
- package/dist/components/link-preview/__tests__/link-preview.integration.test.svelte.js +0 -54
- package/dist/components/link-preview/__tests__/link-preview.test.svelte.js +0 -215
- package/dist/components/list/__tests__/list.test.svelte.js +0 -22
- package/dist/components/menubar/__tests__/MenubarTest.svelte +0 -52
- package/dist/components/menubar/__tests__/menubar.a11y.test.js +0 -28
- package/dist/components/menubar/__tests__/menubar.integration.test.svelte.js +0 -57
- package/dist/components/menubar/__tests__/menubar.test.svelte.js +0 -328
- package/dist/components/meter/__tests__/meter.test.svelte.js +0 -26
- package/dist/components/nav-bar/__tests__/nav-bar.test.svelte.js +0 -23
- package/dist/components/nav-drawer/__tests__/nav-drawer.test.svelte.js +0 -28
- package/dist/components/nav-rail/__tests__/nav-rail.test.svelte.js +0 -28
- package/dist/components/pagination/__tests__/pagination.test.svelte.js +0 -26
- package/dist/components/pin-input/__tests__/pin-input.test.svelte.js +0 -71
- package/dist/components/popover/__tests__/PopoverTest.svelte +0 -27
- package/dist/components/popover/__tests__/popover.test.svelte.js +0 -196
- package/dist/components/progress/__tests__/progress.test.svelte.js +0 -32
- package/dist/components/radio-group/__tests__/radio-group.test.svelte.js +0 -22
- package/dist/components/range-calendar/__tests__/RangeCalendarTest.svelte +0 -23
- package/dist/components/range-calendar/__tests__/range-calendar.test.svelte.js +0 -105
- package/dist/components/rating-group/__tests__/rating-group.test.svelte.js +0 -26
- package/dist/components/scroll-area/__tests__/scroll-area.test.svelte.js +0 -34
- package/dist/components/search-field/__tests__/SearchFieldTest.svelte +0 -34
- package/dist/components/search-field/__tests__/search-field.test.svelte.js +0 -118
- package/dist/components/select/__tests__/SelectLabelPersistenceTest.svelte +0 -31
- package/dist/components/select/__tests__/SelectTest.svelte +0 -33
- package/dist/components/select/__tests__/select.test.svelte.js +0 -221
- package/dist/components/separator/__tests__/separator.test.svelte.js +0 -39
- package/dist/components/sheet/__tests__/SheetTest.svelte +0 -34
- package/dist/components/sheet/__tests__/sheet.test.svelte.js +0 -193
- package/dist/components/side-sheet/__tests__/side-sheet.test.svelte.js +0 -138
- package/dist/components/sidebar/__tests__/SidebarFullTest.svelte +0 -45
- package/dist/components/sidebar/__tests__/SidebarTest.svelte +0 -9
- package/dist/components/sidebar/__tests__/sidebar.test.svelte.js +0 -186
- package/dist/components/simple-dialog/__tests__/SimpleDialogTest.svelte +0 -40
- package/dist/components/simple-dialog/__tests__/simple-dialog.test.svelte.js +0 -50
- package/dist/components/simple-tooltip/__tests__/SimpleTooltipTest.svelte +0 -19
- package/dist/components/simple-tooltip/__tests__/simple-tooltip.test.svelte.js +0 -61
- package/dist/components/slider/__tests__/slider.test.svelte.js +0 -42
- package/dist/components/sortable-list/__tests__/NestedSortableListTest.svelte +0 -49
- package/dist/components/sortable-list/__tests__/SortableListTest.svelte +0 -42
- package/dist/components/sortable-list/__tests__/sortable-list.nested.test.svelte.js +0 -112
- package/dist/components/sortable-list/__tests__/sortable-list.test.svelte.js +0 -175
- package/dist/components/splitter/__tests__/splitter.test.svelte.js +0 -26
- package/dist/components/switch/__tests__/switch.test.svelte.js +0 -44
- package/dist/components/table/__tests__/table.test.svelte.js +0 -22
- package/dist/components/tabs/__tests__/TabsTest.svelte +0 -32
- package/dist/components/tabs/__tests__/tabs.test.svelte.js +0 -227
- package/dist/components/textarea/__tests__/textarea.test.svelte.js +0 -55
- package/dist/components/time-field/__tests__/TimeFieldTest.svelte +0 -20
- package/dist/components/time-field/__tests__/time-field.test.svelte.js +0 -100
- package/dist/components/time-range-field/__tests__/TimeRangeFieldTest.svelte +0 -20
- package/dist/components/time-range-field/__tests__/time-range-field.test.svelte.js +0 -124
- package/dist/components/toast/__tests__/toast.test.svelte.js +0 -46
- package/dist/components/toggle/__tests__/toggle.test.svelte.js +0 -38
- package/dist/components/toggle-group/__tests__/toggle-group.test.svelte.js +0 -22
- package/dist/components/toolbar/__tests__/ToolbarCompositionTest.svelte +0 -18
- package/dist/components/toolbar/__tests__/ToolbarTabstopTest.svelte +0 -10
- package/dist/components/toolbar/__tests__/toolbar.test.svelte.js +0 -63
- package/dist/components/tooltip/__tests__/TooltipTest.svelte +0 -22
- package/dist/components/tooltip/__tests__/tooltip.test.svelte.js +0 -225
- package/dist/components/tree/__tests__/TreeLabelContentTest.svelte +0 -21
- package/dist/components/tree/__tests__/TreeTest.svelte +0 -24
- package/dist/components/tree/__tests__/tree.test.svelte.js +0 -229
- package/dist/components/virtualizer/__tests__/waterfall-layout.test.js +0 -82
- package/dist/internal/announcer/__tests__/announcer.test.js +0 -42
- package/dist/internal/dismissible-layer/__tests__/dismissible-layer.test.js +0 -196
- package/dist/internal/drag-drop/__tests__/auto-scroll.test.js +0 -104
- package/dist/internal/drag-drop/__tests__/drag-session.test.svelte.js +0 -803
- package/dist/internal/drag-drop/__tests__/drag-source.test.svelte.js +0 -130
- package/dist/internal/drag-drop/__tests__/drop-delegate.test.js +0 -223
- package/dist/internal/drag-drop/__tests__/types.test.js +0 -20
- package/dist/internal/escape-layer/__tests__/escape-layer.test.js +0 -113
- package/dist/internal/focus-scope/__tests__/focus-scope.test.js +0 -225
- package/dist/internal/grace-area/__tests__/grace-area.test.js +0 -67
- package/dist/internal/helpers/__tests__/helpers.test.js +0 -123
- package/dist/internal/portal/__tests__/PortalTest.svelte +0 -21
- package/dist/internal/portal/__tests__/portal.test.js +0 -29
- package/dist/internal/portal/__tests__/portal.test.svelte.js +0 -57
- package/dist/internal/scroll-lock/__tests__/scroll-lock.test.js +0 -160
- package/dist/internal/typeahead/__tests__/typeahead.test.js +0 -61
- package/src/themes/__tests__/carbon-dark-contrast.test.ts +0 -170
- package/src/themes/__tests__/generated-css-drift.test.ts +0 -53
- package/src/themes/__tests__/scrim-opaque.test.ts +0 -52
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SortableList test suite — T-C-P2-4
|
|
3
|
-
*
|
|
4
|
-
* Coverage strategy:
|
|
5
|
-
* - Smoke, ARIA, orientation, disabled, reorder, group: fully testable in jsdom.
|
|
6
|
-
* - DnD code paths (dragstart, drag, drop, pointer events, DropDelegate spatial logic,
|
|
7
|
-
* AutoScroll): require a real browser — intentionally NOT covered here.
|
|
8
|
-
* These code paths live in:
|
|
9
|
-
* - src/lib/internal/drag-drop/drag-session.svelte.ts
|
|
10
|
-
* - src/lib/internal/drag-drop/drag-source.svelte.ts
|
|
11
|
-
* - src/lib/internal/drag-drop/drop-target.svelte.ts
|
|
12
|
-
* - src/lib/internal/drag-drop/drop-delegate.svelte.ts
|
|
13
|
-
* - src/lib/internal/drag-drop/auto-scroll.svelte.ts
|
|
14
|
-
* Use Playwright (test:e2e) for those code paths.
|
|
15
|
-
* - sortable-list-indicator.svelte is DnD-only (only visible during an active drag session)
|
|
16
|
-
* and therefore has near-zero jsdom coverage — acceptable.
|
|
17
|
-
*/
|
|
18
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
19
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
20
|
-
import SortableListTest from './SortableListTest.svelte';
|
|
21
|
-
// Polyfill browser event types absent from jsdom
|
|
22
|
-
if (typeof PointerEvent === 'undefined') {
|
|
23
|
-
;
|
|
24
|
-
globalThis.PointerEvent = class PointerEvent extends (MouseEvent) {
|
|
25
|
-
constructor(type, init = {}) {
|
|
26
|
-
super(type, init);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
if (typeof DragEvent === 'undefined') {
|
|
31
|
-
;
|
|
32
|
-
globalThis.DragEvent = class DragEvent extends (MouseEvent) {
|
|
33
|
-
dataTransfer;
|
|
34
|
-
constructor(type, init = {}) {
|
|
35
|
-
super(type, init);
|
|
36
|
-
this.dataTransfer = init.dataTransfer ?? null;
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
const DEFAULT_ITEMS = [
|
|
41
|
-
{ id: 'a', label: 'A' },
|
|
42
|
-
{ id: 'b', label: 'B' },
|
|
43
|
-
{ id: 'c', label: 'C' },
|
|
44
|
-
];
|
|
45
|
-
describe('sortableList', () => {
|
|
46
|
-
let target;
|
|
47
|
-
let instance;
|
|
48
|
-
function setup(props = {}) {
|
|
49
|
-
target = document.createElement('div');
|
|
50
|
-
document.body.appendChild(target);
|
|
51
|
-
instance = mount(SortableListTest, { target, props });
|
|
52
|
-
flushSync();
|
|
53
|
-
}
|
|
54
|
-
afterEach(() => {
|
|
55
|
-
if (instance) {
|
|
56
|
-
unmount(instance);
|
|
57
|
-
}
|
|
58
|
-
instance = undefined;
|
|
59
|
-
target?.remove();
|
|
60
|
-
});
|
|
61
|
-
// ── Smoke (5) ───────────────────────────────────────────────────────────
|
|
62
|
-
it('renders the list container', () => {
|
|
63
|
-
setup();
|
|
64
|
-
expect(target.querySelector('[role="listbox"]')).toBeTruthy();
|
|
65
|
-
});
|
|
66
|
-
it('renders all three items', () => {
|
|
67
|
-
setup();
|
|
68
|
-
expect(target.querySelectorAll('[role="option"]').length).toBe(3);
|
|
69
|
-
});
|
|
70
|
-
it('each item has data-dnd-key matching its id', () => {
|
|
71
|
-
setup();
|
|
72
|
-
expect(target.querySelector('[data-dnd-key="a"]')).toBeTruthy();
|
|
73
|
-
expect(target.querySelector('[data-dnd-key="b"]')).toBeTruthy();
|
|
74
|
-
expect(target.querySelector('[data-dnd-key="c"]')).toBeTruthy();
|
|
75
|
-
});
|
|
76
|
-
it('renders a drag handle per item (3 handles)', () => {
|
|
77
|
-
setup();
|
|
78
|
-
expect(target.querySelectorAll('[data-dnd-handle]').length).toBe(3);
|
|
79
|
-
});
|
|
80
|
-
it('renders with expected sp-sortable-list class', () => {
|
|
81
|
-
setup();
|
|
82
|
-
expect(target.querySelector('.sp-sortable-list')).toBeTruthy();
|
|
83
|
-
});
|
|
84
|
-
// ── ARIA (3) ─────────────────────────────────────────────────────────────
|
|
85
|
-
it('aria-label is propagated to the container', () => {
|
|
86
|
-
setup();
|
|
87
|
-
const list = target.querySelector('[role="listbox"]');
|
|
88
|
-
expect(list?.getAttribute('aria-label')).toBe('Sortable test');
|
|
89
|
-
});
|
|
90
|
-
it('aria-orientation reflects the orientation prop', () => {
|
|
91
|
-
setup({ orientation: 'horizontal' });
|
|
92
|
-
const list = target.querySelector('[role="listbox"]');
|
|
93
|
-
expect(list?.getAttribute('aria-orientation')).toBe('horizontal');
|
|
94
|
-
});
|
|
95
|
-
it('items have role="option"', () => {
|
|
96
|
-
setup();
|
|
97
|
-
const items = target.querySelectorAll('[data-dnd-key]');
|
|
98
|
-
for (const item of items) {
|
|
99
|
-
expect(item.getAttribute('role')).toBe('option');
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
// ── Orientation (2) ──────────────────────────────────────────────────────
|
|
103
|
-
it('vertical orientation adds vertical CSS class', () => {
|
|
104
|
-
setup();
|
|
105
|
-
expect(target.querySelector('.sp-sortable-list.vertical')).toBeTruthy();
|
|
106
|
-
});
|
|
107
|
-
it('horizontal orientation adds horizontal CSS class', () => {
|
|
108
|
-
setup({ orientation: 'horizontal' });
|
|
109
|
-
expect(target.querySelector('.sp-sortable-list.horizontal')).toBeTruthy();
|
|
110
|
-
});
|
|
111
|
-
// ── Disabled (2) ─────────────────────────────────────────────────────────
|
|
112
|
-
it('disabled list still renders all items', () => {
|
|
113
|
-
setup({ disabled: true });
|
|
114
|
-
expect(target.querySelectorAll('[role="option"]').length).toBe(3);
|
|
115
|
-
});
|
|
116
|
-
it('items get aria-disabled when list is disabled', () => {
|
|
117
|
-
setup({ disabled: true });
|
|
118
|
-
const items = target.querySelectorAll('[data-dnd-key]');
|
|
119
|
-
expect(items.length).toBe(3);
|
|
120
|
-
for (const item of items) {
|
|
121
|
-
expect(item.getAttribute('aria-disabled')).toBe('true');
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
// ── Items prop / reorder (2) ──────────────────────────────────────────────
|
|
125
|
-
it('renders items in the order provided', () => {
|
|
126
|
-
setup({ items: [...DEFAULT_ITEMS] });
|
|
127
|
-
const keys = Array.from(target.querySelectorAll('[data-dnd-key]')).map((el) => el.getAttribute('data-dnd-key'));
|
|
128
|
-
expect(keys).toEqual(['a', 'b', 'c']);
|
|
129
|
-
});
|
|
130
|
-
it('re-mounting with different item order reflects new DOM order', () => {
|
|
131
|
-
setup({
|
|
132
|
-
items: [
|
|
133
|
-
{ id: 'c', label: 'C' },
|
|
134
|
-
{ id: 'a', label: 'A' },
|
|
135
|
-
{ id: 'b', label: 'B' },
|
|
136
|
-
],
|
|
137
|
-
});
|
|
138
|
-
const keys = Array.from(target.querySelectorAll('[data-dnd-key]')).map((el) => el.getAttribute('data-dnd-key'));
|
|
139
|
-
expect(keys).toEqual(['c', 'a', 'b']);
|
|
140
|
-
});
|
|
141
|
-
// ── Group (1) ────────────────────────────────────────────────────────────
|
|
142
|
-
it('group prop does not throw and container still has data-dnd-target', () => {
|
|
143
|
-
// Group is consumed internally for cross-list DnD context — not reflected as a DOM data-attr.
|
|
144
|
-
// The container always carries data-dnd-target.
|
|
145
|
-
expect(() => setup({ group: 'team-a' })).not.toThrow();
|
|
146
|
-
const list = target.querySelector('[role="listbox"]');
|
|
147
|
-
expect(list?.hasAttribute('data-dnd-target')).toBe(true);
|
|
148
|
-
});
|
|
149
|
-
// ── Empty state (1) ──────────────────────────────────────────────────────
|
|
150
|
-
it('renders no items when items array is empty', () => {
|
|
151
|
-
setup({ items: [] });
|
|
152
|
-
expect(target.querySelectorAll('[data-dnd-key]').length).toBe(0);
|
|
153
|
-
expect(target.querySelector('[role="listbox"]')).toBeTruthy();
|
|
154
|
-
});
|
|
155
|
-
// ── Handles (1) ──────────────────────────────────────────────────────────
|
|
156
|
-
it('handles have aria-hidden="true"', () => {
|
|
157
|
-
setup();
|
|
158
|
-
const handles = target.querySelectorAll('[data-dnd-handle]');
|
|
159
|
-
for (const handle of handles) {
|
|
160
|
-
expect(handle.getAttribute('aria-hidden')).toBe('true');
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
// ── Callbacks (1) ────────────────────────────────────────────────────────
|
|
164
|
-
it('onReorder callback prop is accepted without error', () => {
|
|
165
|
-
const onReorder = vi.fn();
|
|
166
|
-
expect(() => setup({ onReorder })).not.toThrow();
|
|
167
|
-
expect(target.querySelector('[role="listbox"]')).toBeTruthy();
|
|
168
|
-
});
|
|
169
|
-
// ── data-state (1) ───────────────────────────────────────────────────────
|
|
170
|
-
it('container starts with data-state="idle"', () => {
|
|
171
|
-
setup();
|
|
172
|
-
const list = target.querySelector('[role="listbox"]');
|
|
173
|
-
expect(list?.getAttribute('data-state')).toBe('idle');
|
|
174
|
-
});
|
|
175
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import Splitter from '../splitter.svelte';
|
|
4
|
-
describe('splitter', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(Splitter, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders container', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('.sp-splitter')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
it('has separator role', () => {
|
|
23
|
-
setup();
|
|
24
|
-
expect(target.querySelector('[role="separator"]')).toBeTruthy();
|
|
25
|
-
});
|
|
26
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import Switch from '../switch.svelte';
|
|
4
|
-
describe('switch', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(Switch, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role="switch"', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('[role="switch"]')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
it('has aria-checked="false" by default', () => {
|
|
23
|
-
setup();
|
|
24
|
-
expect(target.querySelector('[role="switch"]')?.getAttribute('aria-checked')).toBe('false');
|
|
25
|
-
});
|
|
26
|
-
it('has data-state="unchecked" by default', () => {
|
|
27
|
-
setup();
|
|
28
|
-
expect(target.querySelector('[role="switch"]')?.getAttribute('data-state')).toBe('unchecked');
|
|
29
|
-
});
|
|
30
|
-
it('renders disabled', () => {
|
|
31
|
-
setup({ disabled: true });
|
|
32
|
-
expect(target.querySelector('[role="switch"]')?.hasAttribute('disabled')).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
it('applies aria-label for icon-only usage', () => {
|
|
35
|
-
setup({ ariaLabel: 'Toggle dark mode' });
|
|
36
|
-
expect(target.querySelector('[role="switch"]')?.getAttribute('aria-label')).toBe('Toggle dark mode');
|
|
37
|
-
});
|
|
38
|
-
it('generates hidden input when name is provided', () => {
|
|
39
|
-
setup({ name: 'darkmode', checked: true });
|
|
40
|
-
const hidden = target.querySelector('input[type="hidden"]');
|
|
41
|
-
expect(hidden).toBeTruthy();
|
|
42
|
-
expect(hidden.name).toBe('darkmode');
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import TableRoot from '../components/table-root.svelte';
|
|
4
|
-
describe('table', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(TableRoot, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role=grid', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('[role="grid"]')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { TabsActivationMode, TabsListOverflow } from '../types.js'
|
|
3
|
-
import TabsContent from '../components/tabs-content.svelte'
|
|
4
|
-
import TabsList from '../components/tabs-list.svelte'
|
|
5
|
-
import TabsRoot from '../components/tabs-root.svelte'
|
|
6
|
-
import TabsTrigger from '../components/tabs-trigger.svelte'
|
|
7
|
-
|
|
8
|
-
let {
|
|
9
|
-
value = $bindable('tab1'),
|
|
10
|
-
activationMode = 'automatic' as TabsActivationMode,
|
|
11
|
-
orientation = 'horizontal' as 'horizontal' | 'vertical',
|
|
12
|
-
loop = true,
|
|
13
|
-
overflow,
|
|
14
|
-
}: {
|
|
15
|
-
value?: string
|
|
16
|
-
activationMode?: TabsActivationMode
|
|
17
|
-
orientation?: 'horizontal' | 'vertical'
|
|
18
|
-
loop?: boolean
|
|
19
|
-
overflow?: TabsListOverflow
|
|
20
|
-
} = $props()
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<TabsRoot bind:value {activationMode} {orientation} {loop}>
|
|
24
|
-
<TabsList {overflow}>
|
|
25
|
-
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
|
|
26
|
-
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
|
|
27
|
-
<TabsTrigger value="tab3" disabled>Tab 3</TabsTrigger>
|
|
28
|
-
</TabsList>
|
|
29
|
-
<TabsContent value="tab1">Content 1</TabsContent>
|
|
30
|
-
<TabsContent value="tab2">Content 2</TabsContent>
|
|
31
|
-
<TabsContent value="tab3">Content 3</TabsContent>
|
|
32
|
-
</TabsRoot>
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import TabsRoot from '../components/tabs-root.svelte';
|
|
4
|
-
import TabsTest from './TabsTest.svelte';
|
|
5
|
-
describe('tabs', () => {
|
|
6
|
-
let target;
|
|
7
|
-
let instance;
|
|
8
|
-
function setup(props = {}) {
|
|
9
|
-
target = document.createElement('div');
|
|
10
|
-
document.body.appendChild(target);
|
|
11
|
-
instance = mount(TabsTest, { target, props });
|
|
12
|
-
return target;
|
|
13
|
-
}
|
|
14
|
-
function setupRoot(props = {}) {
|
|
15
|
-
target = document.createElement('div');
|
|
16
|
-
document.body.appendChild(target);
|
|
17
|
-
instance = mount(TabsRoot, { target, props });
|
|
18
|
-
return target;
|
|
19
|
-
}
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
if (instance)
|
|
22
|
-
unmount(instance);
|
|
23
|
-
target?.remove();
|
|
24
|
-
});
|
|
25
|
-
// --- Basic rendering ---
|
|
26
|
-
it('renders container', () => {
|
|
27
|
-
setupRoot();
|
|
28
|
-
expect(target.querySelector('.sp-tabs')).toBeTruthy();
|
|
29
|
-
});
|
|
30
|
-
it('renders tab list with role="tablist"', () => {
|
|
31
|
-
setup();
|
|
32
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
33
|
-
expect(tablist).toBeTruthy();
|
|
34
|
-
});
|
|
35
|
-
it('renders tab triggers with role="tab"', () => {
|
|
36
|
-
setup();
|
|
37
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
38
|
-
expect(tabs.length).toBe(3);
|
|
39
|
-
});
|
|
40
|
-
it('renders active content with role="tabpanel"', () => {
|
|
41
|
-
setup({ value: 'tab1' });
|
|
42
|
-
const panel = target.querySelector('[role="tabpanel"]');
|
|
43
|
-
expect(panel).toBeTruthy();
|
|
44
|
-
});
|
|
45
|
-
// --- ARIA attributes ---
|
|
46
|
-
it('tablist has aria-orientation', () => {
|
|
47
|
-
setup();
|
|
48
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
49
|
-
expect(tablist?.getAttribute('aria-orientation')).toBe('horizontal');
|
|
50
|
-
});
|
|
51
|
-
it('tablist reflects vertical orientation', () => {
|
|
52
|
-
setup({ orientation: 'vertical' });
|
|
53
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
54
|
-
expect(tablist?.getAttribute('aria-orientation')).toBe('vertical');
|
|
55
|
-
});
|
|
56
|
-
it('active tab has aria-selected="true"', () => {
|
|
57
|
-
setup({ value: 'tab1' });
|
|
58
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
59
|
-
expect(tabs[0]?.getAttribute('aria-selected')).toBe('true');
|
|
60
|
-
expect(tabs[1]?.getAttribute('aria-selected')).toBe('false');
|
|
61
|
-
});
|
|
62
|
-
it('active tab has tabindex=0, others have tabindex=-1', () => {
|
|
63
|
-
setup({ value: 'tab1' });
|
|
64
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
65
|
-
expect(tabs[0]?.getAttribute('tabindex')).toBe('0');
|
|
66
|
-
expect(tabs[1]?.getAttribute('tabindex')).toBe('-1');
|
|
67
|
-
});
|
|
68
|
-
it('disabled tab has disabled attribute', () => {
|
|
69
|
-
setup();
|
|
70
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
71
|
-
expect(tabs[2].disabled).toBe(true);
|
|
72
|
-
});
|
|
73
|
-
it('disabled tab has data-disabled', () => {
|
|
74
|
-
setup();
|
|
75
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
76
|
-
expect(tabs[2]?.hasAttribute('data-disabled')).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
// --- Tab state ---
|
|
79
|
-
it('data-state="active" on selected trigger', () => {
|
|
80
|
-
setup({ value: 'tab1' });
|
|
81
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
82
|
-
expect(tabs[0]?.getAttribute('data-state')).toBe('active');
|
|
83
|
-
expect(tabs[1]?.getAttribute('data-state')).toBe('inactive');
|
|
84
|
-
});
|
|
85
|
-
// --- Click activation ---
|
|
86
|
-
it('clicking a tab activates it', () => {
|
|
87
|
-
setup({ value: 'tab1' });
|
|
88
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
89
|
-
tabs[1].click();
|
|
90
|
-
flushSync();
|
|
91
|
-
expect(tabs[1]?.getAttribute('aria-selected')).toBe('true');
|
|
92
|
-
expect(tabs[0]?.getAttribute('aria-selected')).toBe('false');
|
|
93
|
-
});
|
|
94
|
-
it('clicking a tab shows its content panel', () => {
|
|
95
|
-
setup({ value: 'tab1' });
|
|
96
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
97
|
-
tabs[1].click();
|
|
98
|
-
flushSync();
|
|
99
|
-
const panel = target.querySelector('[role="tabpanel"]');
|
|
100
|
-
expect(panel?.textContent).toContain('Content 2');
|
|
101
|
-
});
|
|
102
|
-
// --- Only active panel is rendered ---
|
|
103
|
-
it('only the active panel is in the DOM', () => {
|
|
104
|
-
setup({ value: 'tab1' });
|
|
105
|
-
const panels = target.querySelectorAll('[role="tabpanel"]');
|
|
106
|
-
expect(panels.length).toBe(1);
|
|
107
|
-
expect(panels[0]?.textContent).toContain('Content 1');
|
|
108
|
-
});
|
|
109
|
-
// --- Keyboard navigation (automatic mode) ---
|
|
110
|
-
it('automatic mode: arrow key changes focus and activates tab', () => {
|
|
111
|
-
setup({ value: 'tab1', activationMode: 'automatic' });
|
|
112
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
113
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
114
|
-
// Focus the first tab
|
|
115
|
-
tabs[0].focus();
|
|
116
|
-
flushSync();
|
|
117
|
-
// Press ArrowRight
|
|
118
|
-
tablist.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
|
119
|
-
flushSync();
|
|
120
|
-
// Tab 2 should be active (automatic mode activates on focus)
|
|
121
|
-
expect(tabs[1]?.getAttribute('aria-selected')).toBe('true');
|
|
122
|
-
});
|
|
123
|
-
// --- Manual activation mode ---
|
|
124
|
-
it('manual mode: arrow key moves focus without activating', () => {
|
|
125
|
-
setup({ value: 'tab1', activationMode: 'manual' });
|
|
126
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
127
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
128
|
-
// Focus the first tab
|
|
129
|
-
tabs[0].focus();
|
|
130
|
-
flushSync();
|
|
131
|
-
// Press ArrowRight — focus moves but tab1 stays active
|
|
132
|
-
tablist.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
|
133
|
-
flushSync();
|
|
134
|
-
// Tab 1 should still be selected (manual mode: focus != activation)
|
|
135
|
-
expect(tabs[0]?.getAttribute('aria-selected')).toBe('true');
|
|
136
|
-
// But focus has moved to tab2
|
|
137
|
-
expect(document.activeElement).toBe(tabs[1]);
|
|
138
|
-
});
|
|
139
|
-
// --- Orientation affects keyboard navigation direction ---
|
|
140
|
-
it('vertical orientation uses ArrowDown/ArrowUp for navigation', () => {
|
|
141
|
-
setup({ value: 'tab1', orientation: 'vertical', activationMode: 'automatic' });
|
|
142
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
143
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
144
|
-
tabs[0].focus();
|
|
145
|
-
flushSync();
|
|
146
|
-
// ArrowDown should navigate to next tab (vertical)
|
|
147
|
-
tablist.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
|
|
148
|
-
flushSync();
|
|
149
|
-
expect(tabs[1]?.getAttribute('aria-selected')).toBe('true');
|
|
150
|
-
});
|
|
151
|
-
// -------------------------------------------------------------------------
|
|
152
|
-
// overflow prop (mobile overflow strategy)
|
|
153
|
-
//
|
|
154
|
-
// Default is 'scroll' so long labels never silently truncate; consumers can
|
|
155
|
-
// opt into 'wrap' for multi-row pill layouts. When the active tab changes,
|
|
156
|
-
// the list scrolls it into view via `scrollIntoView` so the user is never
|
|
157
|
-
// looking at an out-of-viewport active tab.
|
|
158
|
-
// -------------------------------------------------------------------------
|
|
159
|
-
describe('overflow prop', () => {
|
|
160
|
-
it('default overflow="scroll" emits data-overflow="scroll" on tablist', () => {
|
|
161
|
-
setup();
|
|
162
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
163
|
-
expect(tablist.getAttribute('data-overflow')).toBe('scroll');
|
|
164
|
-
});
|
|
165
|
-
it('overflow="wrap" emits data-overflow="wrap" on tablist', () => {
|
|
166
|
-
setup({ overflow: 'wrap' });
|
|
167
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
168
|
-
expect(tablist.getAttribute('data-overflow')).toBe('wrap');
|
|
169
|
-
});
|
|
170
|
-
it('vertical + default overflow still emits data-overflow="scroll" alongside data-orientation', () => {
|
|
171
|
-
setup({ orientation: 'vertical' });
|
|
172
|
-
const tablist = target.querySelector('[role="tablist"]');
|
|
173
|
-
expect(tablist.getAttribute('data-overflow')).toBe('scroll');
|
|
174
|
-
expect(tablist.getAttribute('data-orientation')).toBe('vertical');
|
|
175
|
-
});
|
|
176
|
-
it('changing the active tab calls scrollIntoView on the newly active trigger when overflow="scroll"', () => {
|
|
177
|
-
// jsdom does not implement scrollIntoView — install a mock on the
|
|
178
|
-
// prototype so the component's call hits our spy instead of throwing.
|
|
179
|
-
const original = HTMLElement.prototype.scrollIntoView;
|
|
180
|
-
const spy = vi.fn();
|
|
181
|
-
HTMLElement.prototype.scrollIntoView = spy;
|
|
182
|
-
try {
|
|
183
|
-
setup({ value: 'tab1' });
|
|
184
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
185
|
-
spy.mockClear(); // ignore any call from the initial mount
|
|
186
|
-
tabs[1].click();
|
|
187
|
-
flushSync();
|
|
188
|
-
// The spy should have been called on the new active trigger with a
|
|
189
|
-
// sensible config that brings it into the viewport without scrolling
|
|
190
|
-
// the page vertically.
|
|
191
|
-
expect(spy).toHaveBeenCalled();
|
|
192
|
-
const lastCall = spy.mock.calls.at(-1);
|
|
193
|
-
const arg = lastCall?.[0];
|
|
194
|
-
expect(arg).toBeDefined();
|
|
195
|
-
expect(typeof arg).toBe('object');
|
|
196
|
-
expect(arg).toMatchObject({
|
|
197
|
-
inline: expect.stringMatching(/^(nearest|center|start|end)$/),
|
|
198
|
-
block: 'nearest',
|
|
199
|
-
});
|
|
200
|
-
// And the call target was the now-active trigger, not an arbitrary node.
|
|
201
|
-
const callTarget = spy.mock.contexts.at(-1);
|
|
202
|
-
expect(callTarget?.getAttribute('data-state')).toBe('active');
|
|
203
|
-
}
|
|
204
|
-
finally {
|
|
205
|
-
;
|
|
206
|
-
HTMLElement.prototype.scrollIntoView = original;
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
it('overflow="wrap" does NOT call scrollIntoView on tab change (wrap layout shows all tabs)', () => {
|
|
210
|
-
const original = HTMLElement.prototype.scrollIntoView;
|
|
211
|
-
const spy = vi.fn();
|
|
212
|
-
HTMLElement.prototype.scrollIntoView = spy;
|
|
213
|
-
try {
|
|
214
|
-
setup({ value: 'tab1', overflow: 'wrap' });
|
|
215
|
-
const tabs = target.querySelectorAll('[role="tab"]');
|
|
216
|
-
spy.mockClear();
|
|
217
|
-
tabs[1].click();
|
|
218
|
-
flushSync();
|
|
219
|
-
expect(spy).not.toHaveBeenCalled();
|
|
220
|
-
}
|
|
221
|
-
finally {
|
|
222
|
-
;
|
|
223
|
-
HTMLElement.prototype.scrollIntoView = original;
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { dirname, resolve } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { mount, unmount } from 'svelte';
|
|
5
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
6
|
-
import Textarea from '../textarea.svelte';
|
|
7
|
-
const componentSource = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../textarea.svelte'), 'utf8');
|
|
8
|
-
describe('textarea', () => {
|
|
9
|
-
let target;
|
|
10
|
-
let instance;
|
|
11
|
-
function setup(props = {}) {
|
|
12
|
-
target = document.createElement('div');
|
|
13
|
-
document.body.appendChild(target);
|
|
14
|
-
instance = mount(Textarea, { target, props });
|
|
15
|
-
return target;
|
|
16
|
-
}
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
if (instance)
|
|
19
|
-
unmount(instance);
|
|
20
|
-
target?.remove();
|
|
21
|
-
});
|
|
22
|
-
it('renders textarea element', () => {
|
|
23
|
-
setup();
|
|
24
|
-
expect(target.querySelector('textarea')).toBeTruthy();
|
|
25
|
-
});
|
|
26
|
-
it('sets aria-invalid', () => {
|
|
27
|
-
setup({ invalid: true });
|
|
28
|
-
expect(target.querySelector('textarea')?.getAttribute('aria-invalid')).toBe('true');
|
|
29
|
-
});
|
|
30
|
-
// ── iOS Safari focus-zoom contract ────────────────────────────────
|
|
31
|
-
// Real WebKit focus-zoom cannot be reproduced in jsdom. These tests
|
|
32
|
-
// lock the CSS contract that prevents the regression on `size="sm"` :
|
|
33
|
-
// the `sm` typography stays token-driven for desktop, and a clamp to
|
|
34
|
-
// >= 1rem is applied on coarse pointers (touch / iOS). Other sizes
|
|
35
|
-
// inherit the base 1rem and are already iOS-safe.
|
|
36
|
-
describe('iOS Safari focus-zoom contract (size="sm")', () => {
|
|
37
|
-
it('keeps fine-pointer typography on the body-medium token', () => {
|
|
38
|
-
expect(componentSource).toMatch(/\.sp-textarea\.sm\s*\{[^}]*font-size:\s*var\(--sp-sys-typescale-body-medium-size[^}]*\}/);
|
|
39
|
-
});
|
|
40
|
-
it('clamps the sm textarea to at least 1rem on coarse pointers', () => {
|
|
41
|
-
expect(componentSource).toMatch(/@media\s*\(pointer:\s*coarse\)\s*\{[\s\S]*?\.sp-textarea\.sm\s*\{[^}]*font-size:\s*max\(1rem,[^)]*\)[\s\S]*?\}/);
|
|
42
|
-
});
|
|
43
|
-
it('only the sm variant is targeted by the coarse-pointer clamp', () => {
|
|
44
|
-
// The clamp must target `.sp-textarea.sm` and only `.sp-textarea.sm`.
|
|
45
|
-
// Other sizes inherit the base 1rem and must not get an override.
|
|
46
|
-
const coarseRules = [
|
|
47
|
-
...componentSource.matchAll(/@media\s*\(pointer:\s*coarse\)\s*\{[^}]*\{/g),
|
|
48
|
-
];
|
|
49
|
-
expect(coarseRules.length).toBe(1);
|
|
50
|
-
expect(coarseRules[0][0]).toContain('.sp-textarea.sm');
|
|
51
|
-
expect(coarseRules[0][0]).not.toContain('.sp-textarea.md');
|
|
52
|
-
expect(coarseRules[0][0]).not.toContain('.sp-textarea.lg');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { TimeValue } from '../types.js'
|
|
3
|
-
import * as TimeField from '../index.js'
|
|
4
|
-
|
|
5
|
-
let {
|
|
6
|
-
value = $bindable<TimeValue | undefined>(undefined),
|
|
7
|
-
disabled = false,
|
|
8
|
-
readonly: isReadonly = false,
|
|
9
|
-
hourCycle = undefined as 12 | 24 | undefined,
|
|
10
|
-
granularity = 'minute' as 'hour' | 'minute' | 'second',
|
|
11
|
-
}: {
|
|
12
|
-
value?: TimeValue
|
|
13
|
-
disabled?: boolean
|
|
14
|
-
readonly?: boolean
|
|
15
|
-
hourCycle?: 12 | 24
|
|
16
|
-
granularity?: 'hour' | 'minute' | 'second'
|
|
17
|
-
} = $props()
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<TimeField.Root bind:value {disabled} readonly={isReadonly} {hourCycle} {granularity} />
|