@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,221 +0,0 @@
|
|
|
1
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import SelectRoot from '../components/select-root.svelte';
|
|
4
|
-
import SelectLabelPersistenceTest from './SelectLabelPersistenceTest.svelte';
|
|
5
|
-
import SelectTest from './SelectTest.svelte';
|
|
6
|
-
describe('select', () => {
|
|
7
|
-
let target;
|
|
8
|
-
let instance;
|
|
9
|
-
function setup(props = {}) {
|
|
10
|
-
target = document.createElement('div');
|
|
11
|
-
document.body.appendChild(target);
|
|
12
|
-
instance = mount(SelectTest, { target, props: { portalTo: target, ...props } });
|
|
13
|
-
flushSync();
|
|
14
|
-
return target;
|
|
15
|
-
}
|
|
16
|
-
function setupRoot(props = {}) {
|
|
17
|
-
target = document.createElement('div');
|
|
18
|
-
document.body.appendChild(target);
|
|
19
|
-
instance = mount(SelectRoot, { target, props });
|
|
20
|
-
return target;
|
|
21
|
-
}
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
if (instance)
|
|
24
|
-
unmount(instance);
|
|
25
|
-
target?.remove();
|
|
26
|
-
});
|
|
27
|
-
// --- Existing tests ---
|
|
28
|
-
it('renders hidden input when name is provided', () => {
|
|
29
|
-
setupRoot({ name: 'country', value: 'fr' });
|
|
30
|
-
const input = target.querySelector('input[type="hidden"][name="country"]');
|
|
31
|
-
expect(input).toBeTruthy();
|
|
32
|
-
expect(input?.value).toBe('fr');
|
|
33
|
-
});
|
|
34
|
-
it('renders one hidden input per selected value in multiple mode', () => {
|
|
35
|
-
setupRoot({ type: 'multiple', name: 'tags', value: ['a', 'b'] });
|
|
36
|
-
const inputs = target.querySelectorAll('input[type="hidden"][name="tags"]');
|
|
37
|
-
expect(inputs).toHaveLength(2);
|
|
38
|
-
});
|
|
39
|
-
it('keeps selected item label casing when content unmounts', async () => {
|
|
40
|
-
target = document.createElement('div');
|
|
41
|
-
document.body.appendChild(target);
|
|
42
|
-
instance = mount(SelectLabelPersistenceTest, { target, props: { portalTo: target } });
|
|
43
|
-
const trigger = target.querySelector('[data-testid="trigger"]');
|
|
44
|
-
trigger?.click();
|
|
45
|
-
await Promise.resolve();
|
|
46
|
-
const blueItem = target.querySelector('[data-value="blue"]');
|
|
47
|
-
expect(blueItem).toBeTruthy();
|
|
48
|
-
blueItem?.click();
|
|
49
|
-
await Promise.resolve();
|
|
50
|
-
const displayValue = target.querySelector('.sp-select-value');
|
|
51
|
-
expect(displayValue?.textContent?.trim()).toBe('Blue');
|
|
52
|
-
const toggleContent = target.querySelector('[data-testid="toggle-content"]');
|
|
53
|
-
toggleContent?.click();
|
|
54
|
-
await Promise.resolve();
|
|
55
|
-
const displayValueAfterUnmount = target.querySelector('.sp-select-value');
|
|
56
|
-
expect(displayValueAfterUnmount?.textContent?.trim()).toBe('Blue');
|
|
57
|
-
});
|
|
58
|
-
// --- Trigger ARIA ---
|
|
59
|
-
it('trigger has aria-haspopup="listbox"', () => {
|
|
60
|
-
setup();
|
|
61
|
-
const trigger = target.querySelector('.sp-select-trigger');
|
|
62
|
-
expect(trigger?.getAttribute('aria-haspopup')).toBe('listbox');
|
|
63
|
-
});
|
|
64
|
-
it('trigger has aria-expanded="false" when closed', () => {
|
|
65
|
-
setup();
|
|
66
|
-
const trigger = target.querySelector('.sp-select-trigger');
|
|
67
|
-
expect(trigger?.getAttribute('aria-expanded')).toBe('false');
|
|
68
|
-
});
|
|
69
|
-
it('trigger has aria-expanded="true" when open', () => {
|
|
70
|
-
setup({ open: true });
|
|
71
|
-
const trigger = target.querySelector('.sp-select-trigger');
|
|
72
|
-
expect(trigger?.getAttribute('aria-expanded')).toBe('true');
|
|
73
|
-
});
|
|
74
|
-
// --- Content ARIA ---
|
|
75
|
-
it('content has role="listbox" when open', () => {
|
|
76
|
-
setup({ open: true });
|
|
77
|
-
const content = target.querySelector('[role="listbox"]');
|
|
78
|
-
expect(content).toBeTruthy();
|
|
79
|
-
});
|
|
80
|
-
it('content not rendered when closed', () => {
|
|
81
|
-
setup({ open: false });
|
|
82
|
-
expect(target.querySelector('[role="listbox"]')).toBeNull();
|
|
83
|
-
});
|
|
84
|
-
// --- Select items ---
|
|
85
|
-
it('items have role="option"', () => {
|
|
86
|
-
setup({ open: true });
|
|
87
|
-
flushSync();
|
|
88
|
-
const items = target.querySelectorAll('[role="option"]');
|
|
89
|
-
expect(items.length).toBe(20);
|
|
90
|
-
});
|
|
91
|
-
// --- Keyboard: Page Up/Down ---
|
|
92
|
-
it('pageDown jumps 10 items forward', () => {
|
|
93
|
-
setup({ open: true });
|
|
94
|
-
flushSync();
|
|
95
|
-
const content = target.querySelector('[role="listbox"]');
|
|
96
|
-
const items = content.querySelectorAll('[data-roving-focus-item]');
|
|
97
|
-
// Focus first item
|
|
98
|
-
items[0].focus();
|
|
99
|
-
expect(document.activeElement).toBe(items[0]);
|
|
100
|
-
// Press PageDown
|
|
101
|
-
content.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageDown', bubbles: true }));
|
|
102
|
-
flushSync();
|
|
103
|
-
// Should jump to item at index 10
|
|
104
|
-
expect(document.activeElement).toBe(items[10]);
|
|
105
|
-
});
|
|
106
|
-
it('pageUp jumps 10 items backward', () => {
|
|
107
|
-
setup({ open: true });
|
|
108
|
-
flushSync();
|
|
109
|
-
const content = target.querySelector('[role="listbox"]');
|
|
110
|
-
const items = content.querySelectorAll('[data-roving-focus-item]');
|
|
111
|
-
// Focus item at index 15
|
|
112
|
-
items[15].focus();
|
|
113
|
-
expect(document.activeElement).toBe(items[15]);
|
|
114
|
-
// Press PageUp
|
|
115
|
-
content.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageUp', bubbles: true }));
|
|
116
|
-
flushSync();
|
|
117
|
-
// Should jump to item at index 5
|
|
118
|
-
expect(document.activeElement).toBe(items[5]);
|
|
119
|
-
});
|
|
120
|
-
it('pageDown clamps to last item', () => {
|
|
121
|
-
setup({ open: true });
|
|
122
|
-
flushSync();
|
|
123
|
-
const content = target.querySelector('[role="listbox"]');
|
|
124
|
-
const items = content.querySelectorAll('[data-roving-focus-item]');
|
|
125
|
-
// Focus item near the end (index 15)
|
|
126
|
-
items[15].focus();
|
|
127
|
-
// Press PageDown — should clamp to last item (index 19)
|
|
128
|
-
content.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageDown', bubbles: true }));
|
|
129
|
-
flushSync();
|
|
130
|
-
expect(document.activeElement).toBe(items[19]);
|
|
131
|
-
});
|
|
132
|
-
it('pageUp clamps to first item', () => {
|
|
133
|
-
setup({ open: true });
|
|
134
|
-
flushSync();
|
|
135
|
-
const content = target.querySelector('[role="listbox"]');
|
|
136
|
-
const items = content.querySelectorAll('[data-roving-focus-item]');
|
|
137
|
-
// Focus item near the start (index 3)
|
|
138
|
-
items[3].focus();
|
|
139
|
-
// Press PageUp — should clamp to first item (index 0)
|
|
140
|
-
content.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageUp', bubbles: true }));
|
|
141
|
-
flushSync();
|
|
142
|
-
expect(document.activeElement).toBe(items[0]);
|
|
143
|
-
});
|
|
144
|
-
// --- Escape key ---
|
|
145
|
-
it('escape closes select', () => {
|
|
146
|
-
setup({ open: true });
|
|
147
|
-
flushSync();
|
|
148
|
-
expect(target.querySelector('[role="listbox"]')).toBeTruthy();
|
|
149
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }));
|
|
150
|
-
flushSync();
|
|
151
|
-
expect(target.querySelector('[role="listbox"]')).toBeNull();
|
|
152
|
-
});
|
|
153
|
-
// --- onOpenChange callback ---
|
|
154
|
-
it('calls onOpenChange with new value on each open transition', () => {
|
|
155
|
-
const calls = [];
|
|
156
|
-
// Use setup so afterEach handles cleanup and instance is properly tracked.
|
|
157
|
-
setup({
|
|
158
|
-
open: false,
|
|
159
|
-
onOpenChange: (v) => calls.push(v),
|
|
160
|
-
});
|
|
161
|
-
const trigger = target.querySelector('.sp-select-trigger');
|
|
162
|
-
trigger.click();
|
|
163
|
-
flushSync();
|
|
164
|
-
expect(calls).toEqual([true]);
|
|
165
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }));
|
|
166
|
-
flushSync();
|
|
167
|
-
expect(calls).toEqual([true, false]);
|
|
168
|
-
});
|
|
169
|
-
describe('portal opt-out (disablePortal)', () => {
|
|
170
|
-
it('portals to document.body by default', () => {
|
|
171
|
-
const localTarget = document.createElement('div');
|
|
172
|
-
document.body.appendChild(localTarget);
|
|
173
|
-
const localInstance = mount(SelectTest, {
|
|
174
|
-
target: localTarget,
|
|
175
|
-
props: { open: true },
|
|
176
|
-
});
|
|
177
|
-
flushSync();
|
|
178
|
-
try {
|
|
179
|
-
expect(localTarget.querySelector('.sp-select-content')).toBeNull();
|
|
180
|
-
expect(document.body.querySelector('.sp-select-content')).toBeTruthy();
|
|
181
|
-
}
|
|
182
|
-
finally {
|
|
183
|
-
unmount(localInstance);
|
|
184
|
-
localTarget.remove();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
it('renders inline when disablePortal={true}', () => {
|
|
188
|
-
const localTarget = document.createElement('div');
|
|
189
|
-
document.body.appendChild(localTarget);
|
|
190
|
-
const localInstance = mount(SelectTest, {
|
|
191
|
-
target: localTarget,
|
|
192
|
-
props: { open: true, disablePortal: true },
|
|
193
|
-
});
|
|
194
|
-
flushSync();
|
|
195
|
-
try {
|
|
196
|
-
expect(localTarget.querySelector('.sp-select-content')).toBeTruthy();
|
|
197
|
-
}
|
|
198
|
-
finally {
|
|
199
|
-
unmount(localInstance);
|
|
200
|
-
localTarget.remove();
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
it('preserves consumer class on the content element when inline', () => {
|
|
204
|
-
const localTarget = document.createElement('div');
|
|
205
|
-
document.body.appendChild(localTarget);
|
|
206
|
-
const localInstance = mount(SelectTest, {
|
|
207
|
-
target: localTarget,
|
|
208
|
-
props: { open: true, disablePortal: true, contentClass: 'my-consumer-class' },
|
|
209
|
-
});
|
|
210
|
-
flushSync();
|
|
211
|
-
try {
|
|
212
|
-
const el = localTarget.querySelector('.sp-select-content.my-consumer-class');
|
|
213
|
-
expect(el).toBeTruthy();
|
|
214
|
-
}
|
|
215
|
-
finally {
|
|
216
|
-
unmount(localInstance);
|
|
217
|
-
localTarget.remove();
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import Separator from '../separator.svelte';
|
|
4
|
-
describe('separator', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(Separator, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role="separator" by default', () => {
|
|
19
|
-
setup();
|
|
20
|
-
const sep = target.querySelector('[role="separator"]');
|
|
21
|
-
expect(sep).toBeTruthy();
|
|
22
|
-
});
|
|
23
|
-
it('has aria-orientation="horizontal" by default', () => {
|
|
24
|
-
setup();
|
|
25
|
-
const sep = target.querySelector('[role="separator"]');
|
|
26
|
-
expect(sep?.getAttribute('aria-orientation')).toBe('horizontal');
|
|
27
|
-
});
|
|
28
|
-
it('supports vertical orientation', () => {
|
|
29
|
-
setup({ orientation: 'vertical' });
|
|
30
|
-
const sep = target.querySelector('[role="separator"]');
|
|
31
|
-
expect(sep?.getAttribute('aria-orientation')).toBe('vertical');
|
|
32
|
-
});
|
|
33
|
-
it('renders as decorative (role="none") when decorative=true', () => {
|
|
34
|
-
setup({ decorative: true });
|
|
35
|
-
const sep = target.querySelector('[role="none"]');
|
|
36
|
-
expect(sep).toBeTruthy();
|
|
37
|
-
expect(sep?.getAttribute('aria-hidden')).toBe('true');
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import SheetClose from '../components/sheet-close.svelte'
|
|
3
|
-
import SheetContent from '../components/sheet-content.svelte'
|
|
4
|
-
import SheetDescription from '../components/sheet-description.svelte'
|
|
5
|
-
import SheetOverlay from '../components/sheet-overlay.svelte'
|
|
6
|
-
import SheetRoot from '../components/sheet-root.svelte'
|
|
7
|
-
import SheetTitle from '../components/sheet-title.svelte'
|
|
8
|
-
import SheetTrigger from '../components/sheet-trigger.svelte'
|
|
9
|
-
|
|
10
|
-
let {
|
|
11
|
-
open = $bindable(false),
|
|
12
|
-
trapFocus = true,
|
|
13
|
-
preventScroll = true,
|
|
14
|
-
portalTo = undefined,
|
|
15
|
-
side = 'right' as const,
|
|
16
|
-
}: {
|
|
17
|
-
open?: boolean
|
|
18
|
-
trapFocus?: boolean
|
|
19
|
-
preventScroll?: boolean
|
|
20
|
-
portalTo?: HTMLElement | string
|
|
21
|
-
side?: 'top' | 'right' | 'bottom' | 'left'
|
|
22
|
-
} = $props()
|
|
23
|
-
</script>
|
|
24
|
-
|
|
25
|
-
<SheetRoot bind:open>
|
|
26
|
-
<SheetTrigger>Open Sheet</SheetTrigger>
|
|
27
|
-
<SheetOverlay />
|
|
28
|
-
<SheetContent {trapFocus} {preventScroll} {portalTo} {side}>
|
|
29
|
-
<SheetTitle>Test Title</SheetTitle>
|
|
30
|
-
<SheetDescription>Test Description</SheetDescription>
|
|
31
|
-
<button data-testid="inside-button">Inside</button>
|
|
32
|
-
<SheetClose>Close</SheetClose>
|
|
33
|
-
</SheetContent>
|
|
34
|
-
</SheetRoot>
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import SheetRoot from '../components/sheet-root.svelte';
|
|
4
|
-
import SheetTest from './SheetTest.svelte';
|
|
5
|
-
describe('sheet', () => {
|
|
6
|
-
let target;
|
|
7
|
-
let instance;
|
|
8
|
-
function setup(props = {}) {
|
|
9
|
-
target = document.createElement('div');
|
|
10
|
-
document.body.appendChild(target);
|
|
11
|
-
// Pass portalTo=target by default so existing tests keep querying `target`
|
|
12
|
-
instance = mount(SheetTest, { target, props: { portalTo: target, ...props } });
|
|
13
|
-
flushSync();
|
|
14
|
-
return target;
|
|
15
|
-
}
|
|
16
|
-
function setupRoot(props = {}) {
|
|
17
|
-
target = document.createElement('div');
|
|
18
|
-
document.body.appendChild(target);
|
|
19
|
-
instance = mount(SheetRoot, { target, props });
|
|
20
|
-
return target;
|
|
21
|
-
}
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
if (instance)
|
|
24
|
-
unmount(instance);
|
|
25
|
-
target?.remove();
|
|
26
|
-
});
|
|
27
|
-
// --- Basic rendering ---
|
|
28
|
-
it('renders root', () => {
|
|
29
|
-
setupRoot();
|
|
30
|
-
expect(target.querySelector('.sp-sheet-root')).toBeTruthy();
|
|
31
|
-
});
|
|
32
|
-
it('data-state is closed by default', () => {
|
|
33
|
-
setupRoot();
|
|
34
|
-
expect(target.querySelector('.sp-sheet-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-sheet-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-sheet-trigger');
|
|
45
|
-
expect(trigger?.getAttribute('aria-expanded')).toBe('false');
|
|
46
|
-
});
|
|
47
|
-
it('trigger has aria-controls pointing to content id', () => {
|
|
48
|
-
setup({ open: true });
|
|
49
|
-
const trigger = target.querySelector('.sp-sheet-trigger');
|
|
50
|
-
const content = target.querySelector('[role="dialog"]');
|
|
51
|
-
expect(trigger?.getAttribute('aria-controls')).toBe(content?.id);
|
|
52
|
-
});
|
|
53
|
-
it('trigger toggles open state on click', () => {
|
|
54
|
-
setup();
|
|
55
|
-
const trigger = target.querySelector('.sp-sheet-trigger');
|
|
56
|
-
trigger.click();
|
|
57
|
-
flushSync();
|
|
58
|
-
expect(target.querySelector('.sp-sheet-root')?.getAttribute('data-state')).toBe('open');
|
|
59
|
-
});
|
|
60
|
-
// --- Content ARIA ---
|
|
61
|
-
it('content has role="dialog"', () => {
|
|
62
|
-
setup({ open: true });
|
|
63
|
-
expect(target.querySelector('[role="dialog"]')).toBeTruthy();
|
|
64
|
-
});
|
|
65
|
-
it('content has aria-modal="true"', () => {
|
|
66
|
-
setup({ open: true });
|
|
67
|
-
const content = target.querySelector('[role="dialog"]');
|
|
68
|
-
expect(content?.getAttribute('aria-modal')).toBe('true');
|
|
69
|
-
});
|
|
70
|
-
it('content has aria-labelledby pointing to title', () => {
|
|
71
|
-
setup({ open: true });
|
|
72
|
-
const content = target.querySelector('[role="dialog"]');
|
|
73
|
-
const title = target.querySelector('.sp-sheet-title');
|
|
74
|
-
expect(content?.getAttribute('aria-labelledby')).toBe(title?.id);
|
|
75
|
-
});
|
|
76
|
-
it('content has aria-describedby pointing to description', () => {
|
|
77
|
-
setup({ open: true });
|
|
78
|
-
const content = target.querySelector('[role="dialog"]');
|
|
79
|
-
const desc = target.querySelector('.sp-sheet-description');
|
|
80
|
-
expect(content?.getAttribute('aria-describedby')).toBe(desc?.id);
|
|
81
|
-
});
|
|
82
|
-
it('content has data-state="open" when open', () => {
|
|
83
|
-
setup({ open: true });
|
|
84
|
-
const content = target.querySelector('[role="dialog"]');
|
|
85
|
-
expect(content?.getAttribute('data-state')).toBe('open');
|
|
86
|
-
});
|
|
87
|
-
// --- Content visibility ---
|
|
88
|
-
it('content is not rendered when closed', () => {
|
|
89
|
-
setup({ open: false });
|
|
90
|
-
expect(target.querySelector('[role="dialog"]')).toBeNull();
|
|
91
|
-
});
|
|
92
|
-
it('content is rendered when open', () => {
|
|
93
|
-
setup({ open: true });
|
|
94
|
-
expect(target.querySelector('[role="dialog"]')).toBeTruthy();
|
|
95
|
-
});
|
|
96
|
-
// --- Overlay ---
|
|
97
|
-
it('overlay has aria-hidden="true"', () => {
|
|
98
|
-
setup({ open: true });
|
|
99
|
-
const overlay = target.querySelector('.sp-sheet-overlay');
|
|
100
|
-
expect(overlay?.getAttribute('aria-hidden')).toBe('true');
|
|
101
|
-
});
|
|
102
|
-
// --- Close button ---
|
|
103
|
-
it('close button closes the sheet', () => {
|
|
104
|
-
setup({ open: true });
|
|
105
|
-
const closeBtn = target.querySelector('.sp-sheet-close');
|
|
106
|
-
closeBtn.click();
|
|
107
|
-
flushSync();
|
|
108
|
-
expect(target.querySelector('[role="dialog"]')).toBeNull();
|
|
109
|
-
});
|
|
110
|
-
// --- Escape key ---
|
|
111
|
-
it('escape key closes the sheet', () => {
|
|
112
|
-
setup({ open: true });
|
|
113
|
-
// Flush to ensure $effect that mounts EscapeLayerState has run
|
|
114
|
-
flushSync();
|
|
115
|
-
expect(target.querySelector('[role="dialog"]')).toBeTruthy();
|
|
116
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }));
|
|
117
|
-
flushSync();
|
|
118
|
-
expect(target.querySelector('[role="dialog"]')).toBeNull();
|
|
119
|
-
});
|
|
120
|
-
// --- Title ---
|
|
121
|
-
it('title renders as h2 by default', () => {
|
|
122
|
-
setup({ open: true });
|
|
123
|
-
const title = target.querySelector('.sp-sheet-title');
|
|
124
|
-
expect(title?.tagName).toBe('H2');
|
|
125
|
-
});
|
|
126
|
-
// --- Description ---
|
|
127
|
-
it('description renders as <p>', () => {
|
|
128
|
-
setup({ open: true });
|
|
129
|
-
const desc = target.querySelector('.sp-sheet-description');
|
|
130
|
-
expect(desc?.tagName).toBe('P');
|
|
131
|
-
});
|
|
132
|
-
// --- Trigger aria-expanded updates ---
|
|
133
|
-
it('trigger aria-expanded becomes true when sheet opens', () => {
|
|
134
|
-
setup();
|
|
135
|
-
const trigger = target.querySelector('.sp-sheet-trigger');
|
|
136
|
-
expect(trigger.getAttribute('aria-expanded')).toBe('false');
|
|
137
|
-
trigger.click();
|
|
138
|
-
flushSync();
|
|
139
|
-
expect(trigger.getAttribute('aria-expanded')).toBe('true');
|
|
140
|
-
});
|
|
141
|
-
// --- Portal ---
|
|
142
|
-
it('portals content to document.body by default when open', () => {
|
|
143
|
-
const t = document.createElement('div');
|
|
144
|
-
t.id = 'sheet-host';
|
|
145
|
-
document.body.appendChild(t);
|
|
146
|
-
const inst = mount(SheetTest, { target: t, props: { open: true } });
|
|
147
|
-
flushSync();
|
|
148
|
-
try {
|
|
149
|
-
expect(t.querySelector('[role="dialog"]')).toBeNull();
|
|
150
|
-
expect(document.body.querySelector('[role="dialog"]')).toBeTruthy();
|
|
151
|
-
}
|
|
152
|
-
finally {
|
|
153
|
-
unmount(inst);
|
|
154
|
-
t.remove();
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
it('renders content inline when portalTo is explicitly set to the host', () => {
|
|
158
|
-
const t = document.createElement('div');
|
|
159
|
-
document.body.appendChild(t);
|
|
160
|
-
const inst = mount(SheetTest, {
|
|
161
|
-
target: t,
|
|
162
|
-
props: { open: true, portalTo: t },
|
|
163
|
-
});
|
|
164
|
-
flushSync();
|
|
165
|
-
try {
|
|
166
|
-
expect(t.querySelector('[role="dialog"]')).toBeTruthy();
|
|
167
|
-
}
|
|
168
|
-
finally {
|
|
169
|
-
unmount(inst);
|
|
170
|
-
t.remove();
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
// --- Side prop ---
|
|
174
|
-
it('sets data-side attribute matching prop', () => {
|
|
175
|
-
const t = document.createElement('div');
|
|
176
|
-
document.body.appendChild(t);
|
|
177
|
-
const inst = mount(SheetTest, { target: t, props: { open: true, portalTo: t, side: 'left' } });
|
|
178
|
-
flushSync();
|
|
179
|
-
try {
|
|
180
|
-
const content = t.querySelector('[role="dialog"]');
|
|
181
|
-
expect(content?.getAttribute('data-side')).toBe('left');
|
|
182
|
-
}
|
|
183
|
-
finally {
|
|
184
|
-
unmount(inst);
|
|
185
|
-
t.remove();
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
it('defaults data-side to "right"', () => {
|
|
189
|
-
setup({ open: true });
|
|
190
|
-
const content = target.querySelector('[role="dialog"]');
|
|
191
|
-
expect(content?.getAttribute('data-side')).toBe('right');
|
|
192
|
-
});
|
|
193
|
-
});
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
|
3
|
-
import SideSheet from '../side-sheet.svelte';
|
|
4
|
-
// jsdom does not implement PointerEvent — polyfill it so tests can dispatch
|
|
5
|
-
// pointerdown events (the dismissible layer listens to these).
|
|
6
|
-
beforeAll(() => {
|
|
7
|
-
if (typeof globalThis.PointerEvent === 'undefined') {
|
|
8
|
-
;
|
|
9
|
-
globalThis.PointerEvent = class PointerEvent extends MouseEvent {
|
|
10
|
-
pointerId;
|
|
11
|
-
pointerType;
|
|
12
|
-
constructor(type, init = {}) {
|
|
13
|
-
super(type, init);
|
|
14
|
-
this.pointerId = init.pointerId ?? 0;
|
|
15
|
-
this.pointerType =
|
|
16
|
-
init.pointerType ?? 'mouse';
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
describe('side-sheet', () => {
|
|
22
|
-
let target;
|
|
23
|
-
let instance = null;
|
|
24
|
-
function setup(props = {}) {
|
|
25
|
-
target = document.createElement('div');
|
|
26
|
-
document.body.appendChild(target);
|
|
27
|
-
instance = mount(SideSheet, { target, props: { open: true, ...props } });
|
|
28
|
-
flushSync();
|
|
29
|
-
return target;
|
|
30
|
-
}
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
if (instance) {
|
|
33
|
-
unmount(instance);
|
|
34
|
-
instance = null;
|
|
35
|
-
}
|
|
36
|
-
target?.remove();
|
|
37
|
-
// Clear body styles set by ScrollLockState between tests
|
|
38
|
-
document.body.style.overflow = '';
|
|
39
|
-
document.body.style.paddingRight = '';
|
|
40
|
-
});
|
|
41
|
-
// --- Rendering (baseline) ---
|
|
42
|
-
it('does not render when open is false', () => {
|
|
43
|
-
setup({ open: false });
|
|
44
|
-
expect(target.querySelector('.sp-side-sheet')).toBeNull();
|
|
45
|
-
});
|
|
46
|
-
it('renders when open is true', () => {
|
|
47
|
-
setup({ open: true });
|
|
48
|
-
expect(target.querySelector('.sp-side-sheet')).toBeTruthy();
|
|
49
|
-
});
|
|
50
|
-
it('has role="dialog"', () => {
|
|
51
|
-
setup({ open: true });
|
|
52
|
-
expect(target.querySelector('[role="dialog"]')).toBeTruthy();
|
|
53
|
-
});
|
|
54
|
-
it('applies placement="end" by default', () => {
|
|
55
|
-
setup({ open: true });
|
|
56
|
-
expect(target.querySelector('.sp-side-sheet.end')).toBeTruthy();
|
|
57
|
-
});
|
|
58
|
-
it('applies placement="start" when requested', () => {
|
|
59
|
-
setup({ open: true, placement: 'start' });
|
|
60
|
-
expect(target.querySelector('.sp-side-sheet.start')).toBeTruthy();
|
|
61
|
-
});
|
|
62
|
-
// --- Mode standard (no modal behavior) ---
|
|
63
|
-
it('standard mode does not render overlay', () => {
|
|
64
|
-
setup({ open: true, mode: 'standard' });
|
|
65
|
-
expect(target.querySelector('.sp-side-sheet-overlay')).toBeNull();
|
|
66
|
-
});
|
|
67
|
-
it('standard mode does not set aria-modal', () => {
|
|
68
|
-
setup({ open: true, mode: 'standard' });
|
|
69
|
-
const sheet = target.querySelector('[role="dialog"]');
|
|
70
|
-
expect(sheet?.hasAttribute('aria-modal')).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
it('standard mode does not lock body scroll', () => {
|
|
73
|
-
setup({ open: true, mode: 'standard' });
|
|
74
|
-
expect(document.body.style.overflow).not.toBe('hidden');
|
|
75
|
-
});
|
|
76
|
-
// --- Mode modal (baseline behavior) ---
|
|
77
|
-
it('modal mode renders overlay', () => {
|
|
78
|
-
setup({ open: true, mode: 'modal' });
|
|
79
|
-
expect(target.querySelector('.sp-side-sheet-overlay')).toBeTruthy();
|
|
80
|
-
});
|
|
81
|
-
it('modal mode sets aria-modal="true"', () => {
|
|
82
|
-
setup({ open: true, mode: 'modal' });
|
|
83
|
-
const sheet = target.querySelector('[role="dialog"]');
|
|
84
|
-
expect(sheet?.getAttribute('aria-modal')).toBe('true');
|
|
85
|
-
});
|
|
86
|
-
it('modal mode locks body scroll', () => {
|
|
87
|
-
setup({ open: true, mode: 'modal' });
|
|
88
|
-
expect(document.body.style.overflow).toBe('hidden');
|
|
89
|
-
});
|
|
90
|
-
it('overlay click closes in modal mode', () => {
|
|
91
|
-
setup({ open: true, mode: 'modal' });
|
|
92
|
-
const overlay = target.querySelector('.sp-side-sheet-overlay');
|
|
93
|
-
overlay.click();
|
|
94
|
-
flushSync();
|
|
95
|
-
expect(target.querySelector('.sp-side-sheet')).toBeNull();
|
|
96
|
-
});
|
|
97
|
-
it('escape key closes in modal mode', () => {
|
|
98
|
-
setup({ open: true, mode: 'modal' });
|
|
99
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
100
|
-
flushSync();
|
|
101
|
-
expect(target.querySelector('.sp-side-sheet')).toBeNull();
|
|
102
|
-
});
|
|
103
|
-
// --- A11y modal (target behavior — will RED before fix, GREEN after) ---
|
|
104
|
-
it('modal mode: content has tabindex=-1 for focus entry', () => {
|
|
105
|
-
setup({ open: true, mode: 'modal' });
|
|
106
|
-
const sheet = target.querySelector('[role="dialog"]');
|
|
107
|
-
// FocusScopeState will add tabindex=-1 when mounted as focus scope root
|
|
108
|
-
// so the container can receive focus if no tabbable child exists.
|
|
109
|
-
expect(sheet.getAttribute('tabindex')).toBe('-1');
|
|
110
|
-
});
|
|
111
|
-
it('modal mode: pointerdown outside content closes the sheet (dismissible layer)', () => {
|
|
112
|
-
setup({ open: true, mode: 'modal' });
|
|
113
|
-
// Click on body outside the sheet (not the overlay — the overlay is inside target)
|
|
114
|
-
const outsideEl = document.createElement('button');
|
|
115
|
-
document.body.appendChild(outsideEl);
|
|
116
|
-
outsideEl.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }));
|
|
117
|
-
flushSync();
|
|
118
|
-
expect(target.querySelector('.sp-side-sheet')).toBeNull();
|
|
119
|
-
outsideEl.remove();
|
|
120
|
-
});
|
|
121
|
-
it('calls onOpenChange(false) when Escape is pressed in modal mode', () => {
|
|
122
|
-
const calls = [];
|
|
123
|
-
target = document.createElement('div');
|
|
124
|
-
document.body.appendChild(target);
|
|
125
|
-
instance = mount(SideSheet, {
|
|
126
|
-
target,
|
|
127
|
-
props: {
|
|
128
|
-
open: true,
|
|
129
|
-
mode: 'modal',
|
|
130
|
-
onOpenChange: (open) => calls.push(open),
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
flushSync();
|
|
134
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
135
|
-
flushSync();
|
|
136
|
-
expect(calls).toEqual([false]);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import * as Sidebar from '../index.js'
|
|
3
|
-
|
|
4
|
-
let {
|
|
5
|
-
open = $bindable(true),
|
|
6
|
-
collapsible = 'icon' as 'offcanvas' | 'icon' | 'none',
|
|
7
|
-
}: {
|
|
8
|
-
open?: boolean
|
|
9
|
-
collapsible?: 'offcanvas' | 'icon' | 'none'
|
|
10
|
-
} = $props()
|
|
11
|
-
</script>
|
|
12
|
-
|
|
13
|
-
<!-- Note: Sidebar.Root does not forward extra attributes (no rest props in SidebarRootProps),
|
|
14
|
-
so data-testid is NOT forwarded to the <aside>. We identify the root via .sp-sidebar class.
|
|
15
|
-
All other components that accept children/slots can use data-testid on their inner elements. -->
|
|
16
|
-
<Sidebar.Provider bind:open>
|
|
17
|
-
<Sidebar.Root {collapsible}>
|
|
18
|
-
<Sidebar.Header>
|
|
19
|
-
<h2 data-testid="sidebar-header">App</h2>
|
|
20
|
-
</Sidebar.Header>
|
|
21
|
-
<Sidebar.Content>
|
|
22
|
-
<Sidebar.Group>
|
|
23
|
-
<Sidebar.GroupLabel>Main</Sidebar.GroupLabel>
|
|
24
|
-
<Sidebar.GroupContent>
|
|
25
|
-
<Sidebar.Menu>
|
|
26
|
-
<Sidebar.MenuItem>
|
|
27
|
-
<Sidebar.MenuButton data-testid="menu-home">Home</Sidebar.MenuButton>
|
|
28
|
-
</Sidebar.MenuItem>
|
|
29
|
-
<Sidebar.MenuItem>
|
|
30
|
-
<Sidebar.MenuButton data-testid="menu-settings">Settings</Sidebar.MenuButton>
|
|
31
|
-
</Sidebar.MenuItem>
|
|
32
|
-
</Sidebar.Menu>
|
|
33
|
-
</Sidebar.GroupContent>
|
|
34
|
-
</Sidebar.Group>
|
|
35
|
-
<Sidebar.Separator />
|
|
36
|
-
</Sidebar.Content>
|
|
37
|
-
<Sidebar.Footer>
|
|
38
|
-
<span data-testid="sidebar-footer">Footer</span>
|
|
39
|
-
</Sidebar.Footer>
|
|
40
|
-
</Sidebar.Root>
|
|
41
|
-
<Sidebar.Trigger data-testid="sidebar-trigger">Toggle</Sidebar.Trigger>
|
|
42
|
-
<Sidebar.Inset>
|
|
43
|
-
<main data-testid="inset-content">Page content</main>
|
|
44
|
-
</Sidebar.Inset>
|
|
45
|
-
</Sidebar.Provider>
|