@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,196 +0,0 @@
|
|
|
1
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import PopoverRoot from '../components/popover-root.svelte';
|
|
4
|
-
import PopoverTest from './PopoverTest.svelte';
|
|
5
|
-
describe('popover', () => {
|
|
6
|
-
let target;
|
|
7
|
-
let instance;
|
|
8
|
-
function setup(props = {}) {
|
|
9
|
-
target = document.createElement('div');
|
|
10
|
-
document.body.appendChild(target);
|
|
11
|
-
instance = mount(PopoverTest, { target, props: { portalTo: target, ...props } });
|
|
12
|
-
flushSync();
|
|
13
|
-
return target;
|
|
14
|
-
}
|
|
15
|
-
function setupRoot(props = {}) {
|
|
16
|
-
target = document.createElement('div');
|
|
17
|
-
document.body.appendChild(target);
|
|
18
|
-
instance = mount(PopoverRoot, { target, props });
|
|
19
|
-
return target;
|
|
20
|
-
}
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
if (instance)
|
|
23
|
-
unmount(instance);
|
|
24
|
-
target?.remove();
|
|
25
|
-
});
|
|
26
|
-
// --- Basic rendering ---
|
|
27
|
-
it('renders root', () => {
|
|
28
|
-
setupRoot();
|
|
29
|
-
expect(target.querySelector('.sp-popover-root')).toBeTruthy();
|
|
30
|
-
});
|
|
31
|
-
it('root has data-state="closed" by default', () => {
|
|
32
|
-
setup();
|
|
33
|
-
const root = target.querySelector('.sp-popover-root');
|
|
34
|
-
expect(root?.getAttribute('data-state')).toBe('closed');
|
|
35
|
-
});
|
|
36
|
-
// --- Trigger ARIA ---
|
|
37
|
-
it('trigger has aria-haspopup="dialog"', () => {
|
|
38
|
-
setup();
|
|
39
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
40
|
-
expect(trigger?.getAttribute('aria-haspopup')).toBe('dialog');
|
|
41
|
-
});
|
|
42
|
-
it('trigger has aria-expanded="false" when closed', () => {
|
|
43
|
-
setup();
|
|
44
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
45
|
-
expect(trigger?.getAttribute('aria-expanded')).toBe('false');
|
|
46
|
-
});
|
|
47
|
-
it('trigger has aria-expanded="true" when open', () => {
|
|
48
|
-
setup({ open: true });
|
|
49
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
50
|
-
expect(trigger?.getAttribute('aria-expanded')).toBe('true');
|
|
51
|
-
});
|
|
52
|
-
it('trigger has type="button"', () => {
|
|
53
|
-
setup();
|
|
54
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
55
|
-
expect(trigger?.type).toBe('button');
|
|
56
|
-
});
|
|
57
|
-
it('trigger has aria-controls when open', () => {
|
|
58
|
-
setup({ open: true });
|
|
59
|
-
flushSync();
|
|
60
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
61
|
-
const content = target.querySelector('[role="dialog"]');
|
|
62
|
-
if (content) {
|
|
63
|
-
expect(trigger?.getAttribute('aria-controls')).toBe(content.id);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
it('trigger has no aria-controls when closed', () => {
|
|
67
|
-
setup({ open: false });
|
|
68
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
69
|
-
expect(trigger?.getAttribute('aria-controls')).toBeNull();
|
|
70
|
-
});
|
|
71
|
-
// --- Content ARIA ---
|
|
72
|
-
it('content has role="dialog"', () => {
|
|
73
|
-
setup({ open: true });
|
|
74
|
-
const content = target.querySelector('[role="dialog"]');
|
|
75
|
-
expect(content).toBeTruthy();
|
|
76
|
-
});
|
|
77
|
-
it('content has data-state="open" when open', () => {
|
|
78
|
-
setup({ open: true });
|
|
79
|
-
const content = target.querySelector('[role="dialog"]');
|
|
80
|
-
expect(content?.getAttribute('data-state')).toBe('open');
|
|
81
|
-
});
|
|
82
|
-
it('content is not rendered when closed', () => {
|
|
83
|
-
setup({ open: false });
|
|
84
|
-
expect(target.querySelector('[role="dialog"]')).toBeNull();
|
|
85
|
-
});
|
|
86
|
-
// --- Toggle behavior ---
|
|
87
|
-
it('clicking trigger opens popover', () => {
|
|
88
|
-
setup({ open: false });
|
|
89
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
90
|
-
trigger.click();
|
|
91
|
-
flushSync();
|
|
92
|
-
expect(target.querySelector('.sp-popover-root')?.getAttribute('data-state')).toBe('open');
|
|
93
|
-
});
|
|
94
|
-
it('clicking trigger again closes popover', () => {
|
|
95
|
-
setup({ open: true });
|
|
96
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
97
|
-
trigger.click();
|
|
98
|
-
flushSync();
|
|
99
|
-
expect(target.querySelector('.sp-popover-root')?.getAttribute('data-state')).toBe('closed');
|
|
100
|
-
});
|
|
101
|
-
it('trigger aria-expanded updates on toggle', () => {
|
|
102
|
-
setup({ open: false });
|
|
103
|
-
const trigger = target.querySelector('.sp-popover-trigger');
|
|
104
|
-
expect(trigger.getAttribute('aria-expanded')).toBe('false');
|
|
105
|
-
trigger.click();
|
|
106
|
-
flushSync();
|
|
107
|
-
expect(trigger.getAttribute('aria-expanded')).toBe('true');
|
|
108
|
-
});
|
|
109
|
-
// --- Escape key ---
|
|
110
|
-
it('escape key closes popover', () => {
|
|
111
|
-
setup({ open: true });
|
|
112
|
-
flushSync();
|
|
113
|
-
expect(target.querySelector('[role="dialog"]')).toBeTruthy();
|
|
114
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }));
|
|
115
|
-
flushSync();
|
|
116
|
-
expect(target.querySelector('[role="dialog"]')).toBeNull();
|
|
117
|
-
});
|
|
118
|
-
it('calls onOpenChange with new value on each open transition', () => {
|
|
119
|
-
const calls = [];
|
|
120
|
-
const localTarget = document.createElement('div');
|
|
121
|
-
document.body.appendChild(localTarget);
|
|
122
|
-
const localInstance = mount(PopoverTest, {
|
|
123
|
-
target: localTarget,
|
|
124
|
-
props: {
|
|
125
|
-
open: false,
|
|
126
|
-
onOpenChange: (open) => calls.push(open),
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
flushSync();
|
|
130
|
-
try {
|
|
131
|
-
const trigger = localTarget.querySelector('.sp-popover-trigger');
|
|
132
|
-
trigger.click();
|
|
133
|
-
flushSync();
|
|
134
|
-
expect(calls).toEqual([true]);
|
|
135
|
-
trigger.click();
|
|
136
|
-
flushSync();
|
|
137
|
-
expect(calls).toEqual([true, false]);
|
|
138
|
-
}
|
|
139
|
-
finally {
|
|
140
|
-
unmount(localInstance);
|
|
141
|
-
localTarget.remove();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
describe('portal opt-out (disablePortal)', () => {
|
|
145
|
-
it('portals to document.body by default', () => {
|
|
146
|
-
const localTarget = document.createElement('div');
|
|
147
|
-
document.body.appendChild(localTarget);
|
|
148
|
-
const localInstance = mount(PopoverTest, {
|
|
149
|
-
target: localTarget,
|
|
150
|
-
props: { open: true },
|
|
151
|
-
});
|
|
152
|
-
flushSync();
|
|
153
|
-
try {
|
|
154
|
-
expect(localTarget.querySelector('.sp-popover-content')).toBeNull();
|
|
155
|
-
expect(document.body.querySelector('.sp-popover-content')).toBeTruthy();
|
|
156
|
-
}
|
|
157
|
-
finally {
|
|
158
|
-
unmount(localInstance);
|
|
159
|
-
localTarget.remove();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
it('renders inline when disablePortal={true}', () => {
|
|
163
|
-
const localTarget = document.createElement('div');
|
|
164
|
-
document.body.appendChild(localTarget);
|
|
165
|
-
const localInstance = mount(PopoverTest, {
|
|
166
|
-
target: localTarget,
|
|
167
|
-
props: { open: true, disablePortal: true },
|
|
168
|
-
});
|
|
169
|
-
flushSync();
|
|
170
|
-
try {
|
|
171
|
-
expect(localTarget.querySelector('.sp-popover-content')).toBeTruthy();
|
|
172
|
-
}
|
|
173
|
-
finally {
|
|
174
|
-
unmount(localInstance);
|
|
175
|
-
localTarget.remove();
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
it('preserves consumer class on the content element when inline', () => {
|
|
179
|
-
const localTarget = document.createElement('div');
|
|
180
|
-
document.body.appendChild(localTarget);
|
|
181
|
-
const localInstance = mount(PopoverTest, {
|
|
182
|
-
target: localTarget,
|
|
183
|
-
props: { open: true, disablePortal: true, contentClass: 'my-consumer-class' },
|
|
184
|
-
});
|
|
185
|
-
flushSync();
|
|
186
|
-
try {
|
|
187
|
-
const el = localTarget.querySelector('.sp-popover-content.my-consumer-class');
|
|
188
|
-
expect(el).toBeTruthy();
|
|
189
|
-
}
|
|
190
|
-
finally {
|
|
191
|
-
unmount(localInstance);
|
|
192
|
-
localTarget.remove();
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import Progress from '../progress.svelte';
|
|
4
|
-
describe('progress', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(Progress, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role="progressbar"', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('[role="progressbar"]')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
it('has aria-valuenow when value is set', () => {
|
|
23
|
-
setup({ value: 60, max: 100 });
|
|
24
|
-
const bar = target.querySelector('[role="progressbar"]');
|
|
25
|
-
expect(bar.getAttribute('aria-valuenow')).toBe('60');
|
|
26
|
-
expect(bar.getAttribute('aria-valuemax')).toBe('100');
|
|
27
|
-
});
|
|
28
|
-
it('applies color class', () => {
|
|
29
|
-
setup({ color: 'success' });
|
|
30
|
-
expect(target.querySelector('[role="progressbar"]')?.className).toContain('success');
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import RadioGroupRoot from '../components/radio-group-root.svelte';
|
|
4
|
-
describe('radio-group', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(RadioGroupRoot, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role=radiogroup', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('[role="radiogroup"]')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { DateRange, DateValue } from '../../../internal/date-time/types.js'
|
|
3
|
-
import * as RangeCalendar from '../index.js'
|
|
4
|
-
|
|
5
|
-
let {
|
|
6
|
-
value = $bindable<DateRange>({}),
|
|
7
|
-
placeholder = $bindable<DateValue | undefined>(undefined),
|
|
8
|
-
disabled = false,
|
|
9
|
-
readonly: isReadonly = false,
|
|
10
|
-
locale = 'en-US',
|
|
11
|
-
}: {
|
|
12
|
-
value?: DateRange
|
|
13
|
-
placeholder?: DateValue
|
|
14
|
-
disabled?: boolean
|
|
15
|
-
readonly?: boolean
|
|
16
|
-
locale?: string
|
|
17
|
-
} = $props()
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<RangeCalendar.Root bind:value bind:placeholder {disabled} readonly={isReadonly} {locale}>
|
|
21
|
-
<RangeCalendar.Header />
|
|
22
|
-
<RangeCalendar.Grid />
|
|
23
|
-
</RangeCalendar.Root>
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { CalendarDate } from '@internationalized/date';
|
|
2
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
3
|
-
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
|
4
|
-
import RangeCalendarTest from './RangeCalendarTest.svelte';
|
|
5
|
-
beforeAll(() => {
|
|
6
|
-
if (typeof PointerEvent === 'undefined') {
|
|
7
|
-
;
|
|
8
|
-
globalThis.PointerEvent = class PointerEvent extends MouseEvent {
|
|
9
|
-
constructor(type, init = {}) {
|
|
10
|
-
super(type, init);
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
describe('rangeCalendar', () => {
|
|
16
|
-
let target;
|
|
17
|
-
let instance;
|
|
18
|
-
function setup(props = {}) {
|
|
19
|
-
target = document.createElement('div');
|
|
20
|
-
document.body.appendChild(target);
|
|
21
|
-
instance = mount(RangeCalendarTest, { target, props });
|
|
22
|
-
flushSync();
|
|
23
|
-
}
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
if (instance)
|
|
26
|
-
unmount(instance);
|
|
27
|
-
instance = undefined;
|
|
28
|
-
target?.remove();
|
|
29
|
-
});
|
|
30
|
-
it('renders grid', () => {
|
|
31
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
32
|
-
expect(target.querySelector('[role="grid"], [role="group"]')).toBeTruthy();
|
|
33
|
-
});
|
|
34
|
-
it('renders day cells', () => {
|
|
35
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
36
|
-
expect(target.querySelectorAll('[role="gridcell"], button[data-value]').length).toBeGreaterThan(20);
|
|
37
|
-
});
|
|
38
|
-
it('heading shows current month', () => {
|
|
39
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
40
|
-
expect(target.textContent ?? '').toMatch(/April|Avr|Apr|2026/i);
|
|
41
|
-
});
|
|
42
|
-
it('initial range marks start and end as selected', () => {
|
|
43
|
-
setup({ value: { start: new CalendarDate(2026, 4, 10), end: new CalendarDate(2026, 4, 20) } });
|
|
44
|
-
// data-range-start and data-range-end mark the endpoints
|
|
45
|
-
const starts = target.querySelectorAll('[data-range-start], [data-selected]');
|
|
46
|
-
const ends = target.querySelectorAll('[data-range-end], [aria-selected="true"]');
|
|
47
|
-
expect(starts.length + ends.length).toBeGreaterThanOrEqual(2);
|
|
48
|
-
});
|
|
49
|
-
it('initial range highlights cells in-between', () => {
|
|
50
|
-
setup({ value: { start: new CalendarDate(2026, 4, 10), end: new CalendarDate(2026, 4, 20) } });
|
|
51
|
-
expect(target.querySelector('[data-in-range], [data-range-middle], [data-highlighted]')).toBeTruthy();
|
|
52
|
-
});
|
|
53
|
-
it('first click sets the range start', () => {
|
|
54
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
55
|
-
const cell = target.querySelector('[role="gridcell"] button:not([disabled]), button[data-value]:not([disabled])');
|
|
56
|
-
cell?.click();
|
|
57
|
-
flushSync();
|
|
58
|
-
expect(target.querySelector('[data-selected], [aria-selected="true"], [data-range-start]')).toBeTruthy();
|
|
59
|
-
});
|
|
60
|
-
it('second click completes the range', () => {
|
|
61
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
62
|
-
const cells = target.querySelectorAll('[role="gridcell"] button:not([disabled]), button[data-value]:not([disabled])');
|
|
63
|
-
cells[5]?.click();
|
|
64
|
-
flushSync();
|
|
65
|
-
cells[15]?.click();
|
|
66
|
-
flushSync();
|
|
67
|
-
// At least start + end = 2 selected markers
|
|
68
|
-
expect(target.querySelectorAll('[data-selected], [aria-selected="true"], [data-range-start], [data-range-end]').length).toBeGreaterThanOrEqual(2);
|
|
69
|
-
});
|
|
70
|
-
it('hovering after first click shows preview', () => {
|
|
71
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
72
|
-
const cells = target.querySelectorAll('[role="gridcell"] button:not([disabled]), button[data-value]:not([disabled])');
|
|
73
|
-
cells[5]?.click();
|
|
74
|
-
flushSync();
|
|
75
|
-
cells[10]?.dispatchEvent(new PointerEvent('pointerenter', { bubbles: true }));
|
|
76
|
-
flushSync();
|
|
77
|
-
// After hover, in-range preview should appear
|
|
78
|
-
expect(target.querySelector('[data-in-range], [data-highlighted], [data-hovered], [data-preview]')).toBeTruthy();
|
|
79
|
-
});
|
|
80
|
-
it('disabled ignores clicks', () => {
|
|
81
|
-
setup({ disabled: true, placeholder: new CalendarDate(2026, 4, 13) });
|
|
82
|
-
target.querySelector('[role="gridcell"] button, button[data-value]')?.click();
|
|
83
|
-
flushSync();
|
|
84
|
-
expect(target.querySelector('[data-selected], [aria-selected="true"], [data-range-start]')).toBeNull();
|
|
85
|
-
});
|
|
86
|
-
it('readonly ignores clicks', () => {
|
|
87
|
-
setup({ readonly: true, placeholder: new CalendarDate(2026, 4, 13) });
|
|
88
|
-
target.querySelector('[role="gridcell"] button, button[data-value]')?.click();
|
|
89
|
-
flushSync();
|
|
90
|
-
expect(target.querySelector('[data-selected], [aria-selected="true"], [data-range-start]')).toBeNull();
|
|
91
|
-
});
|
|
92
|
-
it('next-month advances heading', () => {
|
|
93
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
94
|
-
const before = target.textContent ?? '';
|
|
95
|
-
target
|
|
96
|
-
.querySelector('button[aria-label*="next" i], button[aria-label*="Next" i], button[data-calendar-next]')
|
|
97
|
-
?.click();
|
|
98
|
-
flushSync();
|
|
99
|
-
expect(target.textContent ?? '').not.toBe(before);
|
|
100
|
-
});
|
|
101
|
-
it('grid has aria-label', () => {
|
|
102
|
-
setup({ placeholder: new CalendarDate(2026, 4, 13) });
|
|
103
|
-
expect(target.querySelector('[role="grid"]')?.getAttribute('aria-label')).toBeTruthy();
|
|
104
|
-
});
|
|
105
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import RatingGroup from '../rating-group.svelte';
|
|
4
|
-
describe('rating-group', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(RatingGroup, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role=slider', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('[role="slider"]')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
it('has aria-valuemax', () => {
|
|
23
|
-
setup();
|
|
24
|
-
expect(target.querySelector('[role="slider"]')?.getAttribute('aria-valuemax')).toBe('5');
|
|
25
|
-
});
|
|
26
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import ScrollArea from '../scroll-area.svelte';
|
|
4
|
-
describe('scroll-area', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(ScrollArea, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('uses hover mode by default', () => {
|
|
19
|
-
setup();
|
|
20
|
-
const root = target.querySelector('.sp-scroll-area');
|
|
21
|
-
expect(root?.getAttribute('data-type')).toBe('hover');
|
|
22
|
-
});
|
|
23
|
-
it('accepts explicit mode', () => {
|
|
24
|
-
setup({ type: 'always' });
|
|
25
|
-
const root = target.querySelector('.sp-scroll-area');
|
|
26
|
-
expect(root?.getAttribute('data-type')).toBe('always');
|
|
27
|
-
});
|
|
28
|
-
it('renders shell, viewport and content wrappers', () => {
|
|
29
|
-
setup();
|
|
30
|
-
expect(target.querySelector('.sp-scroll-area-shell')).toBeTruthy();
|
|
31
|
-
expect(target.querySelector('.sp-scroll-area-viewport')).toBeTruthy();
|
|
32
|
-
expect(target.querySelector('.sp-scroll-area-content')).toBeTruthy();
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import SearchField from '../search-field.svelte'
|
|
3
|
-
|
|
4
|
-
let value = $state('')
|
|
5
|
-
const disabled = false
|
|
6
|
-
const placeholder = 'Search...'
|
|
7
|
-
const size = 'md' as const
|
|
8
|
-
let clearCount = $state(0)
|
|
9
|
-
let inputCount = $state(0)
|
|
10
|
-
|
|
11
|
-
function handleClear() {
|
|
12
|
-
clearCount++
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function handleInput(_v: string) {
|
|
16
|
-
inputCount++
|
|
17
|
-
}
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<SearchField
|
|
21
|
-
bind:value
|
|
22
|
-
{placeholder}
|
|
23
|
-
{size}
|
|
24
|
-
{disabled}
|
|
25
|
-
onClear={handleClear}
|
|
26
|
-
onInput={handleInput}
|
|
27
|
-
aria-label="Test search"
|
|
28
|
-
/>
|
|
29
|
-
|
|
30
|
-
<div style="margin-top: 1rem; font-size: 0.85rem;">
|
|
31
|
-
<p>Value: {value}</p>
|
|
32
|
-
<p>Clear count: {clearCount}</p>
|
|
33
|
-
<p>Input count: {inputCount}</p>
|
|
34
|
-
</div>
|
|
@@ -1,118 +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 SearchField from '../search-field.svelte';
|
|
7
|
-
const componentSource = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../search-field.svelte'), 'utf8');
|
|
8
|
-
describe('searchField', () => {
|
|
9
|
-
let target;
|
|
10
|
-
let instance;
|
|
11
|
-
function setup(props = {}) {
|
|
12
|
-
target = document.createElement('div');
|
|
13
|
-
document.body.appendChild(target);
|
|
14
|
-
instance = mount(SearchField, { target, props });
|
|
15
|
-
return target;
|
|
16
|
-
}
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
if (instance)
|
|
19
|
-
unmount(instance);
|
|
20
|
-
target?.remove();
|
|
21
|
-
});
|
|
22
|
-
it('renders input element', () => {
|
|
23
|
-
setup();
|
|
24
|
-
const input = target.querySelector('input[type="search"]');
|
|
25
|
-
expect(input).toBeTruthy();
|
|
26
|
-
});
|
|
27
|
-
it('renders placeholder text', () => {
|
|
28
|
-
setup({ placeholder: 'Find something...' });
|
|
29
|
-
const input = target.querySelector('input');
|
|
30
|
-
expect(input.placeholder).toBe('Find something...');
|
|
31
|
-
});
|
|
32
|
-
it('accepts initial value', () => {
|
|
33
|
-
setup({ value: 'hello' });
|
|
34
|
-
const input = target.querySelector('input');
|
|
35
|
-
expect(input.value).toBe('hello');
|
|
36
|
-
});
|
|
37
|
-
it('updates bindable value on input event', async () => {
|
|
38
|
-
setup({ value: '' });
|
|
39
|
-
const input = target.querySelector('input');
|
|
40
|
-
input.value = 'typed text';
|
|
41
|
-
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
42
|
-
// The component updates its internal value through the event handler
|
|
43
|
-
expect(input.value).toBe('typed text');
|
|
44
|
-
});
|
|
45
|
-
it('does not show clear button when value is empty', () => {
|
|
46
|
-
setup({ value: '' });
|
|
47
|
-
const clearBtn = target.querySelector('button[aria-label="Clear search"]');
|
|
48
|
-
expect(clearBtn).toBeFalsy();
|
|
49
|
-
});
|
|
50
|
-
it('shows clear button when value is set', () => {
|
|
51
|
-
setup({ value: 'search term' });
|
|
52
|
-
const clearBtn = target.querySelector('button[aria-label="Clear search"]');
|
|
53
|
-
expect(clearBtn).toBeTruthy();
|
|
54
|
-
});
|
|
55
|
-
it('clears value when clear button is clicked', async () => {
|
|
56
|
-
let onClearCalled = false;
|
|
57
|
-
setup({
|
|
58
|
-
value: 'initial text',
|
|
59
|
-
onClear: () => {
|
|
60
|
-
onClearCalled = true;
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
const clearBtn = target.querySelector('button[aria-label="Clear search"]');
|
|
64
|
-
expect(clearBtn).toBeTruthy();
|
|
65
|
-
clearBtn.click();
|
|
66
|
-
// Verify the onClear callback was called
|
|
67
|
-
expect(onClearCalled).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
it('clears value when Escape key is pressed', async () => {
|
|
70
|
-
let onClearCalled = false;
|
|
71
|
-
setup({
|
|
72
|
-
value: 'search text',
|
|
73
|
-
onClear: () => {
|
|
74
|
-
onClearCalled = true;
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
const input = target.querySelector('input');
|
|
78
|
-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
79
|
-
expect(onClearCalled).toBe(true);
|
|
80
|
-
});
|
|
81
|
-
it('applies disabled state to input and clear button', () => {
|
|
82
|
-
setup({ value: 'text', disabled: true });
|
|
83
|
-
const input = target.querySelector('input');
|
|
84
|
-
const clearBtn = target.querySelector('button[aria-label="Clear search"]');
|
|
85
|
-
expect(input.disabled).toBe(true);
|
|
86
|
-
expect(clearBtn?.disabled).toBe(true);
|
|
87
|
-
});
|
|
88
|
-
it('renders search icon', () => {
|
|
89
|
-
setup();
|
|
90
|
-
const icon = target.querySelector('.sp-search-icon');
|
|
91
|
-
expect(icon).toBeTruthy();
|
|
92
|
-
expect(icon?.tagName).toBe('svg');
|
|
93
|
-
});
|
|
94
|
-
it('size="sm" : exposes the input inside the .sm wrapper', () => {
|
|
95
|
-
setup({ size: 'sm' });
|
|
96
|
-
const wrapper = target.querySelector('.sp-search-field.sm');
|
|
97
|
-
expect(wrapper).toBeTruthy();
|
|
98
|
-
expect(wrapper?.querySelector('input.sp-search-input')).toBeTruthy();
|
|
99
|
-
});
|
|
100
|
-
// ── iOS Safari focus-zoom contract ────────────────────────────────
|
|
101
|
-
// Real WebKit focus-zoom cannot be reproduced in jsdom. These tests
|
|
102
|
-
// lock the CSS contract that prevents the regression : (a) no
|
|
103
|
-
// hardcoded magic value bypassing the token system, (b) typography
|
|
104
|
-
// is token-driven for fine pointers, (c) a clamp to >= 1rem is in
|
|
105
|
-
// place on coarse pointers. Visual / real-device validation is
|
|
106
|
-
// tracked by the issue and done manually on iPhone Safari.
|
|
107
|
-
describe('iOS Safari focus-zoom contract (size="sm")', () => {
|
|
108
|
-
it('does not hardcode 0.85rem on the input', () => {
|
|
109
|
-
expect(componentSource).not.toMatch(/font-size:\s*0\.85rem/);
|
|
110
|
-
});
|
|
111
|
-
it('drives the sm input typography from the body-medium token', () => {
|
|
112
|
-
expect(componentSource).toMatch(/\.sp-search-field\.sm\s+\.sp-search-input\s*\{[^}]*font-size:\s*var\(--sp-sys-typescale-body-medium-size[^}]*\}/);
|
|
113
|
-
});
|
|
114
|
-
it('clamps the sm input to at least 1rem on coarse pointers', () => {
|
|
115
|
-
expect(componentSource).toMatch(/@media\s*\(pointer:\s*coarse\)\s*\{[\s\S]*?\.sp-search-field\.sm\s+\.sp-search-input\s*\{[^}]*font-size:\s*max\(1rem,[^)]*\)[\s\S]*?\}/);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import * as Select from '../index.js'
|
|
3
|
-
|
|
4
|
-
const {
|
|
5
|
-
portalTo,
|
|
6
|
-
}: {
|
|
7
|
-
portalTo?: HTMLElement | string
|
|
8
|
-
} = $props()
|
|
9
|
-
|
|
10
|
-
let value = $state('')
|
|
11
|
-
let showContent = $state(true)
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<button
|
|
15
|
-
type="button"
|
|
16
|
-
data-testid="toggle-content"
|
|
17
|
-
onclick={() => {
|
|
18
|
-
showContent = !showContent
|
|
19
|
-
}}
|
|
20
|
-
>
|
|
21
|
-
Toggle content
|
|
22
|
-
</button>
|
|
23
|
-
|
|
24
|
-
<Select.Root bind:value name="color">
|
|
25
|
-
<Select.Trigger data-testid="trigger" placeholder="Pick a color..." />
|
|
26
|
-
{#if showContent}
|
|
27
|
-
<Select.Content {portalTo}>
|
|
28
|
-
<Select.Item value="blue" label="Blue">Blue</Select.Item>
|
|
29
|
-
</Select.Content>
|
|
30
|
-
{/if}
|
|
31
|
-
</Select.Root>
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import SelectContent from '../components/select-content.svelte'
|
|
3
|
-
import SelectItem from '../components/select-item.svelte'
|
|
4
|
-
import SelectRoot from '../components/select-root.svelte'
|
|
5
|
-
import SelectTrigger from '../components/select-trigger.svelte'
|
|
6
|
-
|
|
7
|
-
let {
|
|
8
|
-
open = $bindable(false),
|
|
9
|
-
value = $bindable(''),
|
|
10
|
-
name = '',
|
|
11
|
-
portalTo,
|
|
12
|
-
onOpenChange,
|
|
13
|
-
disablePortal,
|
|
14
|
-
contentClass,
|
|
15
|
-
}: {
|
|
16
|
-
open?: boolean
|
|
17
|
-
value?: string
|
|
18
|
-
name?: string
|
|
19
|
-
portalTo?: HTMLElement | string
|
|
20
|
-
onOpenChange?: (open: boolean) => void
|
|
21
|
-
disablePortal?: boolean
|
|
22
|
-
contentClass?: string
|
|
23
|
-
} = $props()
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<SelectRoot bind:open bind:value {name} {onOpenChange}>
|
|
27
|
-
<SelectTrigger>Select option</SelectTrigger>
|
|
28
|
-
<SelectContent {portalTo} {disablePortal} class={contentClass}>
|
|
29
|
-
{#each Array.from({ length: 20 }, (_, i) => `item-${i + 1}`) as item (item)}
|
|
30
|
-
<SelectItem value={item}>{item}</SelectItem>
|
|
31
|
-
{/each}
|
|
32
|
-
</SelectContent>
|
|
33
|
-
</SelectRoot>
|