@spectral_labs/ui 1.7.0 → 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/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__/CommandInlineDirectTest.svelte +0 -29
- package/dist/components/command/__tests__/CommandInlineTest.svelte +0 -89
- package/dist/components/command/__tests__/CommandTest.svelte +0 -77
- package/dist/components/command/__tests__/command-inline.test.svelte.js +0 -392
- 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,255 +0,0 @@
|
|
|
1
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import ComboboxRoot from '../components/combobox-root.svelte';
|
|
4
|
-
import ComboboxTest from './ComboboxTest.svelte';
|
|
5
|
-
describe('combobox', () => {
|
|
6
|
-
let target;
|
|
7
|
-
let instance;
|
|
8
|
-
function setup(props = {}) {
|
|
9
|
-
target = document.createElement('div');
|
|
10
|
-
document.body.appendChild(target);
|
|
11
|
-
instance = mount(ComboboxTest, { 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(ComboboxRoot, { target, props });
|
|
19
|
-
return target;
|
|
20
|
-
}
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
if (instance)
|
|
23
|
-
unmount(instance);
|
|
24
|
-
target?.remove();
|
|
25
|
-
});
|
|
26
|
-
// --- Existing tests ---
|
|
27
|
-
it('renders closed by default', () => {
|
|
28
|
-
setupRoot();
|
|
29
|
-
const root = target.querySelector('.sp-combobox-root');
|
|
30
|
-
expect(root?.getAttribute('data-state')).toBe('closed');
|
|
31
|
-
});
|
|
32
|
-
it('renders hidden input when name is provided', () => {
|
|
33
|
-
setupRoot({ name: 'fruit', value: 'Apple' });
|
|
34
|
-
const input = target.querySelector('input[type="hidden"][name="fruit"]');
|
|
35
|
-
expect(input).toBeTruthy();
|
|
36
|
-
expect(input?.value).toBe('Apple');
|
|
37
|
-
});
|
|
38
|
-
// --- Input ARIA ---
|
|
39
|
-
it('input has role="combobox"', () => {
|
|
40
|
-
setup();
|
|
41
|
-
const input = target.querySelector('[role="combobox"]');
|
|
42
|
-
expect(input).toBeTruthy();
|
|
43
|
-
});
|
|
44
|
-
it('input has aria-expanded="false" when closed', () => {
|
|
45
|
-
setup();
|
|
46
|
-
const input = target.querySelector('[role="combobox"]');
|
|
47
|
-
expect(input?.getAttribute('aria-expanded')).toBe('false');
|
|
48
|
-
});
|
|
49
|
-
it('input has aria-autocomplete="list"', () => {
|
|
50
|
-
setup();
|
|
51
|
-
const input = target.querySelector('[role="combobox"]');
|
|
52
|
-
expect(input?.getAttribute('aria-autocomplete')).toBe('list');
|
|
53
|
-
});
|
|
54
|
-
it('input has autocomplete="off"', () => {
|
|
55
|
-
setup();
|
|
56
|
-
const input = target.querySelector('[role="combobox"]');
|
|
57
|
-
expect(input?.autocomplete).toBe('off');
|
|
58
|
-
});
|
|
59
|
-
// --- Open on focus ---
|
|
60
|
-
it('opens on input focus', () => {
|
|
61
|
-
setup();
|
|
62
|
-
const input = target.querySelector('[role="combobox"]');
|
|
63
|
-
input.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
|
|
64
|
-
flushSync();
|
|
65
|
-
expect(target.querySelector('.sp-combobox-root')?.getAttribute('data-state')).toBe('open');
|
|
66
|
-
});
|
|
67
|
-
// --- Keyboard navigation ---
|
|
68
|
-
it('arrowDown opens listbox and navigates to next item', () => {
|
|
69
|
-
setup();
|
|
70
|
-
const input = target.querySelector('[role="combobox"]');
|
|
71
|
-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
|
|
72
|
-
flushSync();
|
|
73
|
-
// Should be open
|
|
74
|
-
expect(target.querySelector('.sp-combobox-root')?.getAttribute('data-state')).toBe('open');
|
|
75
|
-
});
|
|
76
|
-
it('escape closes the listbox', () => {
|
|
77
|
-
setup();
|
|
78
|
-
const input = target.querySelector('[role="combobox"]');
|
|
79
|
-
// Open first
|
|
80
|
-
input.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
|
|
81
|
-
flushSync();
|
|
82
|
-
expect(target.querySelector('.sp-combobox-root')?.getAttribute('data-state')).toBe('open');
|
|
83
|
-
// Escape
|
|
84
|
-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
85
|
-
flushSync();
|
|
86
|
-
expect(target.querySelector('.sp-combobox-root')?.getAttribute('data-state')).toBe('closed');
|
|
87
|
-
});
|
|
88
|
-
// --- IME composition guard ---
|
|
89
|
-
it('ignores keydown during IME composition', () => {
|
|
90
|
-
setup();
|
|
91
|
-
const input = target.querySelector('[role="combobox"]');
|
|
92
|
-
// Simulate IME composition event (isComposing: true)
|
|
93
|
-
const event = new KeyboardEvent('keydown', {
|
|
94
|
-
key: 'ArrowDown',
|
|
95
|
-
bubbles: true,
|
|
96
|
-
composed: true,
|
|
97
|
-
});
|
|
98
|
-
// Override isComposing since KeyboardEvent constructor doesn't support it directly
|
|
99
|
-
Object.defineProperty(event, 'isComposing', { value: true });
|
|
100
|
-
input.dispatchEvent(event);
|
|
101
|
-
flushSync();
|
|
102
|
-
// Should NOT have opened because isComposing was true
|
|
103
|
-
expect(target.querySelector('.sp-combobox-root')?.getAttribute('data-state')).toBe('closed');
|
|
104
|
-
});
|
|
105
|
-
it('calls onOpenChange with new value on each open transition', () => {
|
|
106
|
-
const calls = [];
|
|
107
|
-
const localTarget = document.createElement('div');
|
|
108
|
-
document.body.appendChild(localTarget);
|
|
109
|
-
const localInstance = mount(ComboboxTest, {
|
|
110
|
-
target: localTarget,
|
|
111
|
-
props: {
|
|
112
|
-
open: false,
|
|
113
|
-
portalTo: localTarget,
|
|
114
|
-
onOpenChange: (open) => calls.push(open),
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
flushSync();
|
|
118
|
-
try {
|
|
119
|
-
const input = localTarget.querySelector('.sp-combobox-input');
|
|
120
|
-
input.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
|
|
121
|
-
flushSync();
|
|
122
|
-
expect(calls).toEqual([true]);
|
|
123
|
-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
124
|
-
flushSync();
|
|
125
|
-
expect(calls).toEqual([true, false]);
|
|
126
|
-
}
|
|
127
|
-
finally {
|
|
128
|
-
unmount(localInstance);
|
|
129
|
-
localTarget.remove();
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
it('processes keydown when not composing', () => {
|
|
133
|
-
setup();
|
|
134
|
-
const input = target.querySelector('[role="combobox"]');
|
|
135
|
-
const event = new KeyboardEvent('keydown', {
|
|
136
|
-
key: 'ArrowDown',
|
|
137
|
-
bubbles: true,
|
|
138
|
-
});
|
|
139
|
-
Object.defineProperty(event, 'isComposing', { value: false });
|
|
140
|
-
input.dispatchEvent(event);
|
|
141
|
-
flushSync();
|
|
142
|
-
// Should have opened because isComposing was false
|
|
143
|
-
expect(target.querySelector('.sp-combobox-root')?.getAttribute('data-state')).toBe('open');
|
|
144
|
-
});
|
|
145
|
-
describe('content positioning (FloatingLayerState)', () => {
|
|
146
|
-
// Floating UI's computePosition is async; give it a microtask to run.
|
|
147
|
-
async function openAndWaitForFloating(props = {}) {
|
|
148
|
-
setup(props);
|
|
149
|
-
const input = target.querySelector('[role="combobox"]');
|
|
150
|
-
input.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
|
|
151
|
-
flushSync();
|
|
152
|
-
}
|
|
153
|
-
it('applies inline position:absolute on content after opening', async () => {
|
|
154
|
-
await openAndWaitForFloating({ disablePortal: true });
|
|
155
|
-
const content = target.querySelector('.sp-combobox-content');
|
|
156
|
-
await vi.waitFor(() => {
|
|
157
|
-
expect(content.style.position).toBe('absolute');
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
it('applies inline top and left written by Floating UI (not CSS 100%/0)', async () => {
|
|
161
|
-
await openAndWaitForFloating({ disablePortal: true });
|
|
162
|
-
const content = target.querySelector('.sp-combobox-content');
|
|
163
|
-
await vi.waitFor(() => {
|
|
164
|
-
// Floating UI writes pixel values — in jsdom they resolve to '0px'
|
|
165
|
-
// since getBoundingClientRect returns zero, but the values must be
|
|
166
|
-
// *inline styles* (not CSS rules) so the '100%' top and '0' right
|
|
167
|
-
// that caused the bug are gone.
|
|
168
|
-
expect(content.style.top).toMatch(/px$/);
|
|
169
|
-
expect(content.style.left).toMatch(/px$/);
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
it('accepts the side prop (default: bottom)', async () => {
|
|
173
|
-
await openAndWaitForFloating({ disablePortal: true, side: 'top' });
|
|
174
|
-
const content = target.querySelector('.sp-combobox-content');
|
|
175
|
-
expect(content).toBeTruthy();
|
|
176
|
-
});
|
|
177
|
-
it('accepts the sideOffset prop', async () => {
|
|
178
|
-
await openAndWaitForFloating({ disablePortal: true, sideOffset: 12 });
|
|
179
|
-
const content = target.querySelector('.sp-combobox-content');
|
|
180
|
-
expect(content).toBeTruthy();
|
|
181
|
-
});
|
|
182
|
-
it('accepts the align prop', async () => {
|
|
183
|
-
await openAndWaitForFloating({ disablePortal: true, align: 'center' });
|
|
184
|
-
const content = target.querySelector('.sp-combobox-content');
|
|
185
|
-
expect(content).toBeTruthy();
|
|
186
|
-
});
|
|
187
|
-
it('does not stretch to full body width anymore when portaled', async () => {
|
|
188
|
-
// Regression: the bug rendered the content with right:0 against body,
|
|
189
|
-
// producing a full-viewport-width menu. After the fix, the content is
|
|
190
|
-
// positioned (via FloatingLayerState) and does NOT have `right: 0`
|
|
191
|
-
// inherited from the stylesheet.
|
|
192
|
-
await openAndWaitForFloating({});
|
|
193
|
-
const content = document.body.querySelector('.sp-combobox-content');
|
|
194
|
-
await vi.waitFor(() => {
|
|
195
|
-
const cs = window.getComputedStyle(content);
|
|
196
|
-
// Floating UI writes `left` inline → `right` in CSS should not be 0
|
|
197
|
-
// (the old buggy rule `right: 0` is removed). A reasonable proxy:
|
|
198
|
-
// assert that `width` isn't pinned to viewport / body width.
|
|
199
|
-
expect(cs.right).not.toBe('0px');
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
describe('portal opt-out (disablePortal)', () => {
|
|
204
|
-
it('portals to document.body by default', () => {
|
|
205
|
-
const localTarget = document.createElement('div');
|
|
206
|
-
document.body.appendChild(localTarget);
|
|
207
|
-
const localInstance = mount(ComboboxTest, {
|
|
208
|
-
target: localTarget,
|
|
209
|
-
props: { open: true },
|
|
210
|
-
});
|
|
211
|
-
flushSync();
|
|
212
|
-
try {
|
|
213
|
-
expect(localTarget.querySelector('.sp-combobox-content')).toBeNull();
|
|
214
|
-
expect(document.body.querySelector('.sp-combobox-content')).toBeTruthy();
|
|
215
|
-
}
|
|
216
|
-
finally {
|
|
217
|
-
unmount(localInstance);
|
|
218
|
-
localTarget.remove();
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
it('renders inline when disablePortal={true}', () => {
|
|
222
|
-
const localTarget = document.createElement('div');
|
|
223
|
-
document.body.appendChild(localTarget);
|
|
224
|
-
const localInstance = mount(ComboboxTest, {
|
|
225
|
-
target: localTarget,
|
|
226
|
-
props: { open: true, disablePortal: true },
|
|
227
|
-
});
|
|
228
|
-
flushSync();
|
|
229
|
-
try {
|
|
230
|
-
expect(localTarget.querySelector('.sp-combobox-content')).toBeTruthy();
|
|
231
|
-
}
|
|
232
|
-
finally {
|
|
233
|
-
unmount(localInstance);
|
|
234
|
-
localTarget.remove();
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
it('preserves consumer class on the content element when inline', () => {
|
|
238
|
-
const localTarget = document.createElement('div');
|
|
239
|
-
document.body.appendChild(localTarget);
|
|
240
|
-
const localInstance = mount(ComboboxTest, {
|
|
241
|
-
target: localTarget,
|
|
242
|
-
props: { open: true, disablePortal: true, contentClass: 'my-consumer-class' },
|
|
243
|
-
});
|
|
244
|
-
flushSync();
|
|
245
|
-
try {
|
|
246
|
-
const el = localTarget.querySelector('.sp-combobox-content.my-consumer-class');
|
|
247
|
-
expect(el).toBeTruthy();
|
|
248
|
-
}
|
|
249
|
-
finally {
|
|
250
|
-
unmount(localInstance);
|
|
251
|
-
localTarget.remove();
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import CommandInput from '../components/command-input.svelte'
|
|
3
|
-
import CommandItem from '../components/command-item.svelte'
|
|
4
|
-
import CommandList from '../components/command-list.svelte'
|
|
5
|
-
import CommandRoot from '../components/command-root.svelte'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The same inline composition with NO `Command.Group`.
|
|
9
|
-
*
|
|
10
|
-
* The group and the item each gate rendering on their own predicate, and
|
|
11
|
-
* both read the registry. Reviewing #224 turned up two distinct failures
|
|
12
|
-
* behind one symptom, so the group must be removable from the picture for a
|
|
13
|
-
* test to say which mechanism it is exercising.
|
|
14
|
-
*/
|
|
15
|
-
let open = $state(false)
|
|
16
|
-
let value = $state('')
|
|
17
|
-
|
|
18
|
-
export function setOpen(next: boolean) {
|
|
19
|
-
open = next
|
|
20
|
-
}
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<CommandRoot mode="inline" bind:open bind:value>
|
|
24
|
-
<CommandInput class="probe-input" />
|
|
25
|
-
<CommandList>
|
|
26
|
-
<CommandItem value="copy" keywords={['duplicate']}>Copy</CommandItem>
|
|
27
|
-
<CommandItem value="paste">Paste</CommandItem>
|
|
28
|
-
</CommandList>
|
|
29
|
-
</CommandRoot>
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { CommandMode } from '../command.svelte.js'
|
|
3
|
-
import CommandEmpty from '../components/command-empty.svelte'
|
|
4
|
-
import CommandGroup from '../components/command-group.svelte'
|
|
5
|
-
import CommandInput from '../components/command-input.svelte'
|
|
6
|
-
import CommandItem from '../components/command-item.svelte'
|
|
7
|
-
import CommandList from '../components/command-list.svelte'
|
|
8
|
-
import CommandRoot from '../components/command-root.svelte'
|
|
9
|
-
|
|
10
|
-
type Entry = { value: string; label: string; keywords?: string[] }
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Harness for the `inline` composition: a field that outlives its results.
|
|
14
|
-
*
|
|
15
|
-
* The harness OWNS `open` and `entries` as local state and exposes accessors,
|
|
16
|
-
* rather than taking them as props a test would try to reassign — Svelte 5
|
|
17
|
-
* props are not writable through the object `mount()` returns. Owning them
|
|
18
|
-
* here is also closer to real use: `bind:open` lets the Root push a close
|
|
19
|
-
* back (Escape, outside pointer) and the test observes the same value the
|
|
20
|
-
* consumer would.
|
|
21
|
-
*
|
|
22
|
-
* `entries` is mutable so a test can change the COLLECTION between two
|
|
23
|
-
* openings — the case where a stale selection, a dead ARIA reference or a
|
|
24
|
-
* lost first arrow would actually show up.
|
|
25
|
-
*/
|
|
26
|
-
const {
|
|
27
|
-
initialOpen = false,
|
|
28
|
-
initialEntries = [
|
|
29
|
-
{ value: 'copy', label: 'Copy', keywords: ['duplicate'] },
|
|
30
|
-
{ value: 'paste', label: 'Paste' },
|
|
31
|
-
{ value: 'cut', label: 'Cut' },
|
|
32
|
-
] as Entry[],
|
|
33
|
-
mode = 'inline' as CommandMode,
|
|
34
|
-
portalTo = undefined,
|
|
35
|
-
trapFocus = undefined,
|
|
36
|
-
onOpenChange = undefined,
|
|
37
|
-
onEscapeKeydown = undefined,
|
|
38
|
-
onSelect = undefined,
|
|
39
|
-
inputAttrs = undefined,
|
|
40
|
-
}: {
|
|
41
|
-
initialOpen?: boolean
|
|
42
|
-
initialEntries?: Entry[]
|
|
43
|
-
mode?: CommandMode
|
|
44
|
-
portalTo?: HTMLElement | string
|
|
45
|
-
trapFocus?: boolean
|
|
46
|
-
onOpenChange?: (open: boolean) => void
|
|
47
|
-
onEscapeKeydown?: (event: KeyboardEvent) => void
|
|
48
|
-
onSelect?: (value: string) => void
|
|
49
|
-
inputAttrs?: Record<string, unknown>
|
|
50
|
-
} = $props()
|
|
51
|
-
|
|
52
|
-
let open = $state(initialOpen)
|
|
53
|
-
let entries = $state(initialEntries)
|
|
54
|
-
let value = $state('')
|
|
55
|
-
let element = $state<HTMLInputElement | null>(null)
|
|
56
|
-
|
|
57
|
-
export function setOpen(next: boolean) {
|
|
58
|
-
open = next
|
|
59
|
-
}
|
|
60
|
-
export function getOpen() {
|
|
61
|
-
return open
|
|
62
|
-
}
|
|
63
|
-
export function setEntries(next: Entry[]) {
|
|
64
|
-
entries = next
|
|
65
|
-
}
|
|
66
|
-
export function getElement() {
|
|
67
|
-
return element
|
|
68
|
-
}
|
|
69
|
-
</script>
|
|
70
|
-
|
|
71
|
-
<CommandRoot bind:value bind:open {mode} {portalTo} {trapFocus} {onOpenChange} {onEscapeKeydown}>
|
|
72
|
-
<CommandInput
|
|
73
|
-
placeholder="Search..."
|
|
74
|
-
wrapperClass="probe-wrapper"
|
|
75
|
-
class="probe-input"
|
|
76
|
-
bind:element
|
|
77
|
-
{...inputAttrs ?? {}}
|
|
78
|
-
/>
|
|
79
|
-
<CommandList>
|
|
80
|
-
<CommandEmpty>No results found.</CommandEmpty>
|
|
81
|
-
<CommandGroup heading="Actions">
|
|
82
|
-
{#each entries as entry (entry.value)}
|
|
83
|
-
<CommandItem value={entry.value} keywords={entry.keywords} {onSelect}>
|
|
84
|
-
{entry.label}
|
|
85
|
-
</CommandItem>
|
|
86
|
-
{/each}
|
|
87
|
-
</CommandGroup>
|
|
88
|
-
</CommandList>
|
|
89
|
-
</CommandRoot>
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import CommandEmpty from '../components/command-empty.svelte'
|
|
3
|
-
import CommandGroup from '../components/command-group.svelte'
|
|
4
|
-
import CommandInput from '../components/command-input.svelte'
|
|
5
|
-
import CommandItem from '../components/command-item.svelte'
|
|
6
|
-
import CommandList from '../components/command-list.svelte'
|
|
7
|
-
import CommandRoot from '../components/command-root.svelte'
|
|
8
|
-
import CommandSeparator from '../components/command-separator.svelte'
|
|
9
|
-
|
|
10
|
-
let {
|
|
11
|
-
value = $bindable(''),
|
|
12
|
-
shouldFilter = true,
|
|
13
|
-
open = $bindable(true),
|
|
14
|
-
portalTo = undefined,
|
|
15
|
-
closeOnEscape = true,
|
|
16
|
-
closeOnOutsideClick = true,
|
|
17
|
-
onOpenChange = undefined,
|
|
18
|
-
onClose = undefined,
|
|
19
|
-
itemAttrs = undefined,
|
|
20
|
-
disabledItemAttrs = undefined,
|
|
21
|
-
onItemSelect = undefined,
|
|
22
|
-
onDisabledItemSelect = undefined,
|
|
23
|
-
}: {
|
|
24
|
-
value?: string
|
|
25
|
-
shouldFilter?: boolean
|
|
26
|
-
open?: boolean
|
|
27
|
-
portalTo?: HTMLElement | string
|
|
28
|
-
closeOnEscape?: boolean
|
|
29
|
-
closeOnOutsideClick?: boolean
|
|
30
|
-
onOpenChange?: (open: boolean) => void
|
|
31
|
-
onClose?: () => void
|
|
32
|
-
/** Arbitrary HTML attributes / native handlers spread onto the `copy` item. */
|
|
33
|
-
itemAttrs?: Record<string, unknown>
|
|
34
|
-
/** Same, spread onto the `settings` item (the disabled one). */
|
|
35
|
-
disabledItemAttrs?: Record<string, unknown>
|
|
36
|
-
/** Spectral `onSelect` of the `copy` item. */
|
|
37
|
-
onItemSelect?: (value: string) => void
|
|
38
|
-
/** Spectral `onSelect` of the `settings` item. */
|
|
39
|
-
onDisabledItemSelect?: (value: string) => void
|
|
40
|
-
} = $props()
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
|
-
<CommandRoot
|
|
44
|
-
bind:value
|
|
45
|
-
bind:open
|
|
46
|
-
{shouldFilter}
|
|
47
|
-
{portalTo}
|
|
48
|
-
{closeOnEscape}
|
|
49
|
-
{closeOnOutsideClick}
|
|
50
|
-
{onOpenChange}
|
|
51
|
-
{onClose}
|
|
52
|
-
>
|
|
53
|
-
<CommandInput placeholder="Type a command..." />
|
|
54
|
-
<CommandList>
|
|
55
|
-
<CommandEmpty>No results found.</CommandEmpty>
|
|
56
|
-
<CommandGroup heading="Actions">
|
|
57
|
-
<CommandItem
|
|
58
|
-
value="copy"
|
|
59
|
-
keywords={['duplicate']}
|
|
60
|
-
onSelect={onItemSelect}
|
|
61
|
-
{...itemAttrs ?? {}}>Copy</CommandItem
|
|
62
|
-
>
|
|
63
|
-
<CommandItem value="paste">Paste</CommandItem>
|
|
64
|
-
<CommandItem value="cut">Cut</CommandItem>
|
|
65
|
-
</CommandGroup>
|
|
66
|
-
<CommandSeparator />
|
|
67
|
-
<CommandGroup heading="Navigation">
|
|
68
|
-
<CommandItem value="home">Home</CommandItem>
|
|
69
|
-
<CommandItem
|
|
70
|
-
value="settings"
|
|
71
|
-
disabled
|
|
72
|
-
onSelect={onDisabledItemSelect}
|
|
73
|
-
{...disabledItemAttrs ?? {}}>Settings</CommandItem
|
|
74
|
-
>
|
|
75
|
-
</CommandGroup>
|
|
76
|
-
</CommandList>
|
|
77
|
-
</CommandRoot>
|