@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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/THIRD_PARTY_NOTICES.md +201 -0
  3. package/dist/components/command/command.svelte.d.ts +30 -0
  4. package/dist/components/command/command.svelte.d.ts.map +1 -1
  5. package/dist/components/command/command.svelte.js +85 -4
  6. package/dist/components/command/components/command-group.svelte +25 -15
  7. package/dist/components/command/components/command-group.svelte.d.ts.map +1 -1
  8. package/dist/components/command/components/command-input.svelte +54 -5
  9. package/dist/components/command/components/command-input.svelte.d.ts +1 -1
  10. package/dist/components/command/components/command-input.svelte.d.ts.map +1 -1
  11. package/dist/components/command/components/command-list.svelte +22 -3
  12. package/dist/components/command/components/command-list.svelte.d.ts.map +1 -1
  13. package/dist/components/command/components/command-root.svelte +43 -4
  14. package/dist/components/command/components/command-root.svelte.d.ts.map +1 -1
  15. package/dist/components/command/index.d.ts +1 -0
  16. package/dist/components/command/index.d.ts.map +1 -1
  17. package/dist/components/command/types.d.ts +59 -4
  18. package/dist/components/command/types.d.ts.map +1 -1
  19. package/package.json +8 -2
  20. package/dist/components/__tests__/overlay-scrim-source.test.js +0 -134
  21. package/dist/components/accordion/__tests__/AccordionTest.svelte +0 -31
  22. package/dist/components/accordion/__tests__/accordion.test.svelte.js +0 -139
  23. package/dist/components/alert-dialog/__tests__/AlertDialogTest.svelte +0 -28
  24. package/dist/components/alert-dialog/__tests__/alert-dialog.a11y.test.js +0 -42
  25. package/dist/components/alert-dialog/__tests__/alert-dialog.integration.test.svelte.js +0 -51
  26. package/dist/components/alert-dialog/__tests__/alert-dialog.test.svelte.js +0 -113
  27. package/dist/components/app-bar/__tests__/app-bar.test.svelte.js +0 -30
  28. package/dist/components/aspect-ratio/__tests__/aspect-ratio.test.svelte.js +0 -22
  29. package/dist/components/avatar/__tests__/avatar.test.svelte.js +0 -26
  30. package/dist/components/badge/__tests__/badge.test.svelte.js +0 -30
  31. package/dist/components/bottom-sheet/__tests__/BottomSheetTest.svelte +0 -22
  32. package/dist/components/bottom-sheet/__tests__/bottom-sheet.test.svelte.js +0 -83
  33. package/dist/components/button/__tests__/button.test.svelte.js +0 -63
  34. package/dist/components/calendar/__tests__/CalendarTest.svelte +0 -56
  35. package/dist/components/calendar/__tests__/calendar.test.svelte.js +0 -190
  36. package/dist/components/card/__tests__/card.test.svelte.js +0 -30
  37. package/dist/components/checkbox/__tests__/checkbox.test.svelte.js +0 -44
  38. package/dist/components/chips/__tests__/chips.test.svelte.js +0 -26
  39. package/dist/components/collapsible/__tests__/CollapsibleTest.svelte +0 -18
  40. package/dist/components/collapsible/__tests__/collapsible.test.svelte.js +0 -125
  41. package/dist/components/combobox/__tests__/ComboboxTest.svelte +0 -40
  42. package/dist/components/combobox/__tests__/combobox.test.svelte.js +0 -255
  43. package/dist/components/command/__tests__/CommandTest.svelte +0 -77
  44. package/dist/components/command/__tests__/command-item-passthrough.test.svelte.js +0 -284
  45. package/dist/components/command/__tests__/command.a11y.test.js +0 -30
  46. package/dist/components/command/__tests__/command.integration.test.svelte.js +0 -68
  47. package/dist/components/command/__tests__/command.test.svelte.js +0 -230
  48. package/dist/components/context-menu/__tests__/ContextMenuNonInteractiveTest.svelte +0 -32
  49. package/dist/components/context-menu/__tests__/ContextMenuTest.svelte +0 -25
  50. package/dist/components/context-menu/__tests__/context-menu.test.svelte.js +0 -141
  51. package/dist/components/date-field/__tests__/DateFieldTest.svelte +0 -20
  52. package/dist/components/date-field/__tests__/date-field.test.svelte.js +0 -139
  53. package/dist/components/date-picker/__tests__/DatePickerTest.svelte +0 -20
  54. package/dist/components/date-picker/__tests__/date-picker.test.svelte.js +0 -190
  55. package/dist/components/date-range-field/__tests__/DateRangeFieldTest.svelte +0 -20
  56. package/dist/components/date-range-field/__tests__/date-range-field.test.svelte.js +0 -130
  57. package/dist/components/date-range-picker/__tests__/DateRangePickerTest.svelte +0 -18
  58. package/dist/components/date-range-picker/__tests__/date-range-picker.test.svelte.js +0 -156
  59. package/dist/components/dialog/__tests__/DialogTest.svelte +0 -34
  60. package/dist/components/dialog/__tests__/dialog.test.svelte.js +0 -184
  61. package/dist/components/drop-zone/__tests__/drop-zone.test.svelte.js +0 -26
  62. package/dist/components/dropdown-menu/__tests__/DropdownMenuRadioTest.svelte +0 -90
  63. package/dist/components/dropdown-menu/__tests__/DropdownMenuTest.svelte +0 -71
  64. package/dist/components/dropdown-menu/__tests__/dropdown-menu-radio-group.test.svelte.js +0 -306
  65. package/dist/components/dropdown-menu/__tests__/dropdown-menu.test.svelte.js +0 -567
  66. package/dist/components/empty-state/__tests__/empty-state.test.svelte.js +0 -26
  67. package/dist/components/fab/__tests__/fab.test.svelte.js +0 -30
  68. package/dist/components/field/__tests__/FieldTest.svelte +0 -26
  69. package/dist/components/field/__tests__/field.test.svelte.js +0 -74
  70. package/dist/components/grid-list/__tests__/GridListTest.svelte +0 -46
  71. package/dist/components/grid-list/__tests__/grid-list.test.svelte.js +0 -231
  72. package/dist/components/hover-card/__tests__/HoverCardTest.svelte +0 -30
  73. package/dist/components/hover-card/__tests__/hover-card.test.svelte.js +0 -236
  74. package/dist/components/indicator/__tests__/indicator.test.svelte.js +0 -30
  75. package/dist/components/input/__tests__/input.test.svelte.js +0 -67
  76. package/dist/components/label/__tests__/label.test.svelte.js +0 -22
  77. package/dist/components/language-select/__tests__/language-select.test.svelte.js +0 -199
  78. package/dist/components/language-select/__tests__/languages.test.js +0 -87
  79. package/dist/components/link-preview/__tests__/LinkPreviewTest.svelte +0 -37
  80. package/dist/components/link-preview/__tests__/link-preview.a11y.test.js +0 -28
  81. package/dist/components/link-preview/__tests__/link-preview.integration.test.svelte.js +0 -54
  82. package/dist/components/link-preview/__tests__/link-preview.test.svelte.js +0 -215
  83. package/dist/components/list/__tests__/list.test.svelte.js +0 -22
  84. package/dist/components/menubar/__tests__/MenubarTest.svelte +0 -52
  85. package/dist/components/menubar/__tests__/menubar.a11y.test.js +0 -28
  86. package/dist/components/menubar/__tests__/menubar.integration.test.svelte.js +0 -57
  87. package/dist/components/menubar/__tests__/menubar.test.svelte.js +0 -328
  88. package/dist/components/meter/__tests__/meter.test.svelte.js +0 -26
  89. package/dist/components/nav-bar/__tests__/nav-bar.test.svelte.js +0 -23
  90. package/dist/components/nav-drawer/__tests__/nav-drawer.test.svelte.js +0 -28
  91. package/dist/components/nav-rail/__tests__/nav-rail.test.svelte.js +0 -28
  92. package/dist/components/pagination/__tests__/pagination.test.svelte.js +0 -26
  93. package/dist/components/pin-input/__tests__/pin-input.test.svelte.js +0 -71
  94. package/dist/components/popover/__tests__/PopoverTest.svelte +0 -27
  95. package/dist/components/popover/__tests__/popover.test.svelte.js +0 -196
  96. package/dist/components/progress/__tests__/progress.test.svelte.js +0 -32
  97. package/dist/components/radio-group/__tests__/radio-group.test.svelte.js +0 -22
  98. package/dist/components/range-calendar/__tests__/RangeCalendarTest.svelte +0 -23
  99. package/dist/components/range-calendar/__tests__/range-calendar.test.svelte.js +0 -105
  100. package/dist/components/rating-group/__tests__/rating-group.test.svelte.js +0 -26
  101. package/dist/components/scroll-area/__tests__/scroll-area.test.svelte.js +0 -34
  102. package/dist/components/search-field/__tests__/SearchFieldTest.svelte +0 -34
  103. package/dist/components/search-field/__tests__/search-field.test.svelte.js +0 -118
  104. package/dist/components/select/__tests__/SelectLabelPersistenceTest.svelte +0 -31
  105. package/dist/components/select/__tests__/SelectTest.svelte +0 -33
  106. package/dist/components/select/__tests__/select.test.svelte.js +0 -221
  107. package/dist/components/separator/__tests__/separator.test.svelte.js +0 -39
  108. package/dist/components/sheet/__tests__/SheetTest.svelte +0 -34
  109. package/dist/components/sheet/__tests__/sheet.test.svelte.js +0 -193
  110. package/dist/components/side-sheet/__tests__/side-sheet.test.svelte.js +0 -138
  111. package/dist/components/sidebar/__tests__/SidebarFullTest.svelte +0 -45
  112. package/dist/components/sidebar/__tests__/SidebarTest.svelte +0 -9
  113. package/dist/components/sidebar/__tests__/sidebar.test.svelte.js +0 -186
  114. package/dist/components/simple-dialog/__tests__/SimpleDialogTest.svelte +0 -40
  115. package/dist/components/simple-dialog/__tests__/simple-dialog.test.svelte.js +0 -50
  116. package/dist/components/simple-tooltip/__tests__/SimpleTooltipTest.svelte +0 -19
  117. package/dist/components/simple-tooltip/__tests__/simple-tooltip.test.svelte.js +0 -61
  118. package/dist/components/slider/__tests__/slider.test.svelte.js +0 -42
  119. package/dist/components/sortable-list/__tests__/NestedSortableListTest.svelte +0 -49
  120. package/dist/components/sortable-list/__tests__/SortableListTest.svelte +0 -42
  121. package/dist/components/sortable-list/__tests__/sortable-list.nested.test.svelte.js +0 -112
  122. package/dist/components/sortable-list/__tests__/sortable-list.test.svelte.js +0 -175
  123. package/dist/components/splitter/__tests__/splitter.test.svelte.js +0 -26
  124. package/dist/components/switch/__tests__/switch.test.svelte.js +0 -44
  125. package/dist/components/table/__tests__/table.test.svelte.js +0 -22
  126. package/dist/components/tabs/__tests__/TabsTest.svelte +0 -32
  127. package/dist/components/tabs/__tests__/tabs.test.svelte.js +0 -227
  128. package/dist/components/textarea/__tests__/textarea.test.svelte.js +0 -55
  129. package/dist/components/time-field/__tests__/TimeFieldTest.svelte +0 -20
  130. package/dist/components/time-field/__tests__/time-field.test.svelte.js +0 -100
  131. package/dist/components/time-range-field/__tests__/TimeRangeFieldTest.svelte +0 -20
  132. package/dist/components/time-range-field/__tests__/time-range-field.test.svelte.js +0 -124
  133. package/dist/components/toast/__tests__/toast.test.svelte.js +0 -46
  134. package/dist/components/toggle/__tests__/toggle.test.svelte.js +0 -38
  135. package/dist/components/toggle-group/__tests__/toggle-group.test.svelte.js +0 -22
  136. package/dist/components/toolbar/__tests__/ToolbarCompositionTest.svelte +0 -18
  137. package/dist/components/toolbar/__tests__/ToolbarTabstopTest.svelte +0 -10
  138. package/dist/components/toolbar/__tests__/toolbar.test.svelte.js +0 -63
  139. package/dist/components/tooltip/__tests__/TooltipTest.svelte +0 -22
  140. package/dist/components/tooltip/__tests__/tooltip.test.svelte.js +0 -225
  141. package/dist/components/tree/__tests__/TreeLabelContentTest.svelte +0 -21
  142. package/dist/components/tree/__tests__/TreeTest.svelte +0 -24
  143. package/dist/components/tree/__tests__/tree.test.svelte.js +0 -229
  144. package/dist/components/virtualizer/__tests__/waterfall-layout.test.js +0 -82
  145. package/dist/internal/announcer/__tests__/announcer.test.js +0 -42
  146. package/dist/internal/dismissible-layer/__tests__/dismissible-layer.test.js +0 -196
  147. package/dist/internal/drag-drop/__tests__/auto-scroll.test.js +0 -104
  148. package/dist/internal/drag-drop/__tests__/drag-session.test.svelte.js +0 -803
  149. package/dist/internal/drag-drop/__tests__/drag-source.test.svelte.js +0 -130
  150. package/dist/internal/drag-drop/__tests__/drop-delegate.test.js +0 -223
  151. package/dist/internal/drag-drop/__tests__/types.test.js +0 -20
  152. package/dist/internal/escape-layer/__tests__/escape-layer.test.js +0 -113
  153. package/dist/internal/focus-scope/__tests__/focus-scope.test.js +0 -225
  154. package/dist/internal/grace-area/__tests__/grace-area.test.js +0 -67
  155. package/dist/internal/helpers/__tests__/helpers.test.js +0 -123
  156. package/dist/internal/portal/__tests__/PortalTest.svelte +0 -21
  157. package/dist/internal/portal/__tests__/portal.test.js +0 -29
  158. package/dist/internal/portal/__tests__/portal.test.svelte.js +0 -57
  159. package/dist/internal/scroll-lock/__tests__/scroll-lock.test.js +0 -160
  160. package/dist/internal/typeahead/__tests__/typeahead.test.js +0 -61
  161. package/src/themes/__tests__/carbon-dark-contrast.test.ts +0 -170
  162. package/src/themes/__tests__/generated-css-drift.test.ts +0 -53
  163. package/src/themes/__tests__/scrim-opaque.test.ts +0 -52
@@ -1,215 +0,0 @@
1
- import { flushSync, mount, unmount } from 'svelte';
2
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
- import LinkPreviewRoot from '../components/link-preview-root.svelte';
4
- import LinkPreviewTest from './LinkPreviewTest.svelte';
5
- describe('link-preview', () => {
6
- let target;
7
- let instance;
8
- function setup(props = {}) {
9
- target = document.createElement('div');
10
- document.body.appendChild(target);
11
- instance = mount(LinkPreviewTest, { target, props });
12
- return target;
13
- }
14
- function setupRoot(props = {}) {
15
- target = document.createElement('div');
16
- document.body.appendChild(target);
17
- instance = mount(LinkPreviewRoot, { target, props });
18
- return target;
19
- }
20
- afterEach(() => {
21
- vi.useRealTimers();
22
- if (instance)
23
- unmount(instance);
24
- target?.remove();
25
- });
26
- // --- Basic rendering ---
27
- it('renders root', () => {
28
- setupRoot();
29
- expect(target.querySelector('.sp-link-preview-root')).toBeTruthy();
30
- });
31
- it('root has data-state="closed" by default', () => {
32
- setup();
33
- const root = target.querySelector('.sp-link-preview-root');
34
- expect(root?.getAttribute('data-state')).toBe('closed');
35
- });
36
- // --- Trigger ---
37
- it('trigger renders as an anchor', () => {
38
- setup();
39
- const trigger = target.querySelector('.sp-link-preview-trigger');
40
- expect(trigger?.tagName).toBe('A');
41
- });
42
- it('trigger has href attribute', () => {
43
- setup();
44
- const trigger = target.querySelector('.sp-link-preview-trigger');
45
- expect(trigger?.getAttribute('href')).toBe('https://example.com');
46
- });
47
- it('trigger has aria-haspopup="dialog"', () => {
48
- setup();
49
- const trigger = target.querySelector('.sp-link-preview-trigger');
50
- expect(trigger?.getAttribute('aria-haspopup')).toBe('dialog');
51
- });
52
- it('trigger has aria-expanded="false" when closed', () => {
53
- setup();
54
- const trigger = target.querySelector('.sp-link-preview-trigger');
55
- expect(trigger?.getAttribute('aria-expanded')).toBe('false');
56
- });
57
- // --- Content ---
58
- it('content not rendered when closed', () => {
59
- setup();
60
- expect(target.querySelector('[role="dialog"]')).toBeNull();
61
- });
62
- it('content appears on mouseenter with delay', () => {
63
- vi.useFakeTimers();
64
- setup({ openDelay: 0, closeDelay: 0 });
65
- const trigger = target.querySelector('.sp-link-preview-trigger');
66
- trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
67
- vi.advanceTimersByTime(10);
68
- flushSync();
69
- // Content is portalled to document.body, not inside target
70
- const content = document.querySelector('[role="dialog"]');
71
- expect(content).toBeTruthy();
72
- });
73
- it('content has data-state="open" when visible', () => {
74
- vi.useFakeTimers();
75
- setup({ openDelay: 0 });
76
- const trigger = target.querySelector('.sp-link-preview-trigger');
77
- trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
78
- vi.advanceTimersByTime(10);
79
- flushSync();
80
- // Content is portalled to document.body, not inside target
81
- const content = document.querySelector('[role="dialog"]');
82
- expect(content?.getAttribute('data-state')).toBe('open');
83
- });
84
- it('trigger has aria-expanded="true" when content is visible', () => {
85
- vi.useFakeTimers();
86
- setup({ openDelay: 0 });
87
- const trigger = target.querySelector('.sp-link-preview-trigger');
88
- trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
89
- vi.advanceTimersByTime(10);
90
- flushSync();
91
- expect(trigger.getAttribute('aria-expanded')).toBe('true');
92
- });
93
- it('trigger has aria-controls when open', () => {
94
- vi.useFakeTimers();
95
- setup({ openDelay: 0 });
96
- const trigger = target.querySelector('.sp-link-preview-trigger');
97
- trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
98
- vi.advanceTimersByTime(10);
99
- flushSync();
100
- // Content is portalled to document.body, not inside target
101
- const content = document.querySelector('[role="dialog"]');
102
- expect(trigger.getAttribute('aria-controls')).toBe(content?.id);
103
- });
104
- // --- Focus behavior ---
105
- it('opens on focus', () => {
106
- vi.useFakeTimers();
107
- setup({ openDelay: 0 });
108
- const trigger = target.querySelector('.sp-link-preview-trigger');
109
- trigger.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
110
- vi.advanceTimersByTime(10);
111
- flushSync();
112
- // Content is portalled to document.body, not inside target
113
- expect(document.querySelector('[role="dialog"]')).toBeTruthy();
114
- });
115
- it('closes on blur', () => {
116
- vi.useFakeTimers();
117
- setup({ openDelay: 0, closeDelay: 0 });
118
- const trigger = target.querySelector('.sp-link-preview-trigger');
119
- trigger.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
120
- vi.advanceTimersByTime(10);
121
- flushSync();
122
- // Content is portalled to document.body, not inside target
123
- expect(document.querySelector('[role="dialog"]')).toBeTruthy();
124
- trigger.dispatchEvent(new FocusEvent('blur', { bubbles: true }));
125
- vi.advanceTimersByTime(10);
126
- flushSync();
127
- expect(document.querySelector('[role="dialog"]')).toBeNull();
128
- });
129
- describe('portal opt-out (disablePortal)', () => {
130
- beforeEach(() => {
131
- vi.useFakeTimers();
132
- });
133
- it('portals to document.body by default', () => {
134
- const localTarget = document.createElement('div');
135
- document.body.appendChild(localTarget);
136
- const localInstance = mount(LinkPreviewTest, {
137
- target: localTarget,
138
- props: { open: true },
139
- });
140
- flushSync(); // flush $effect (dispatches mouseenter → setTimeout(0))
141
- vi.advanceTimersByTime(0); // fire the 0ms timer → open = true
142
- flushSync(); // re-render content
143
- try {
144
- expect(localTarget.querySelector('.sp-link-preview-content')).toBeNull();
145
- expect(document.body.querySelector('.sp-link-preview-content')).toBeTruthy();
146
- }
147
- finally {
148
- unmount(localInstance);
149
- localTarget.remove();
150
- }
151
- });
152
- it('renders inline when disablePortal={true}', () => {
153
- const localTarget = document.createElement('div');
154
- document.body.appendChild(localTarget);
155
- const localInstance = mount(LinkPreviewTest, {
156
- target: localTarget,
157
- props: { open: true, disablePortal: true },
158
- });
159
- flushSync();
160
- vi.advanceTimersByTime(0);
161
- flushSync();
162
- try {
163
- expect(localTarget.querySelector('.sp-link-preview-content')).toBeTruthy();
164
- }
165
- finally {
166
- unmount(localInstance);
167
- localTarget.remove();
168
- }
169
- });
170
- it('preserves consumer class on the content element when inline', () => {
171
- const localTarget = document.createElement('div');
172
- document.body.appendChild(localTarget);
173
- const localInstance = mount(LinkPreviewTest, {
174
- target: localTarget,
175
- props: { open: true, disablePortal: true, contentClass: 'my-consumer-class' },
176
- });
177
- flushSync();
178
- vi.advanceTimersByTime(0);
179
- flushSync();
180
- try {
181
- const el = localTarget.querySelector('.sp-link-preview-content.my-consumer-class');
182
- expect(el).toBeTruthy();
183
- }
184
- finally {
185
- unmount(localInstance);
186
- localTarget.remove();
187
- }
188
- });
189
- });
190
- it('content is portalled to document.body when open', () => {
191
- vi.useFakeTimers();
192
- const localTarget = document.createElement('div');
193
- document.body.appendChild(localTarget);
194
- const localInstance = mount(LinkPreviewTest, {
195
- target: localTarget,
196
- props: { openDelay: 0, closeDelay: 0 },
197
- });
198
- flushSync();
199
- try {
200
- const trigger = localTarget.querySelector('.sp-link-preview-trigger');
201
- trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
202
- vi.advanceTimersByTime(10);
203
- flushSync();
204
- // Content should NOT be rendered inside the local mount target
205
- expect(localTarget.querySelector('.sp-link-preview-content')).toBeNull();
206
- // Content SHOULD be rendered somewhere in the document (portalled)
207
- expect(document.querySelector('.sp-link-preview-content')).toBeTruthy();
208
- }
209
- finally {
210
- unmount(localInstance);
211
- localTarget.remove();
212
- vi.useRealTimers();
213
- }
214
- });
215
- });
@@ -1,22 +0,0 @@
1
- import { mount, unmount } from 'svelte';
2
- import { afterEach, describe, expect, it } from 'vitest';
3
- import ListRoot from '../components/list-root.svelte';
4
- describe('list', () => {
5
- let target;
6
- let instance;
7
- function setup(props = {}) {
8
- target = document.createElement('div');
9
- document.body.appendChild(target);
10
- instance = mount(ListRoot, { target, props });
11
- return target;
12
- }
13
- afterEach(() => {
14
- if (instance)
15
- unmount(instance);
16
- target?.remove();
17
- });
18
- it('renders with role=list', () => {
19
- setup();
20
- expect(target.querySelector('[role="list"]')).toBeTruthy();
21
- });
22
- });
@@ -1,52 +0,0 @@
1
- <script lang="ts">
2
- import DropdownMenuItem from '../../dropdown-menu/components/dropdown-menu-item.svelte'
3
- import DropdownMenuSeparator from '../../dropdown-menu/components/dropdown-menu-separator.svelte'
4
- import DropdownMenuSubContent from '../../dropdown-menu/components/dropdown-menu-sub-content.svelte'
5
- import DropdownMenuSubTrigger from '../../dropdown-menu/components/dropdown-menu-sub-trigger.svelte'
6
- import DropdownMenuSub from '../../dropdown-menu/components/dropdown-menu-sub.svelte'
7
- import MenubarContent from '../components/menubar-content.svelte'
8
- import MenubarMenu from '../components/menubar-menu.svelte'
9
- import MenubarRoot from '../components/menubar-root.svelte'
10
- import MenubarTrigger from '../components/menubar-trigger.svelte'
11
-
12
- const {
13
- loop = true,
14
- portalTo,
15
- withSub = false,
16
- }: { loop?: boolean; portalTo?: HTMLElement | string; withSub?: boolean } = $props()
17
- </script>
18
-
19
- <MenubarRoot {loop}>
20
- <MenubarMenu value="file">
21
- <MenubarTrigger>File</MenubarTrigger>
22
- <MenubarContent {portalTo}>
23
- <DropdownMenuItem>New</DropdownMenuItem>
24
- <DropdownMenuItem>Open</DropdownMenuItem>
25
- <DropdownMenuSeparator />
26
- <DropdownMenuItem>Save</DropdownMenuItem>
27
- {#if withSub}
28
- <DropdownMenuSub>
29
- <DropdownMenuSubTrigger>Share</DropdownMenuSubTrigger>
30
- <DropdownMenuSubContent>
31
- <DropdownMenuItem>Email</DropdownMenuItem>
32
- <DropdownMenuItem>Copy Link</DropdownMenuItem>
33
- </DropdownMenuSubContent>
34
- </DropdownMenuSub>
35
- {/if}
36
- </MenubarContent>
37
- </MenubarMenu>
38
- <MenubarMenu value="edit">
39
- <MenubarTrigger>Edit</MenubarTrigger>
40
- <MenubarContent {portalTo}>
41
- <DropdownMenuItem>Undo</DropdownMenuItem>
42
- <DropdownMenuItem>Redo</DropdownMenuItem>
43
- </MenubarContent>
44
- </MenubarMenu>
45
- <MenubarMenu value="view">
46
- <MenubarTrigger>View</MenubarTrigger>
47
- <MenubarContent {portalTo}>
48
- <DropdownMenuItem>Zoom In</DropdownMenuItem>
49
- <DropdownMenuItem>Zoom Out</DropdownMenuItem>
50
- </MenubarContent>
51
- </MenubarMenu>
52
- </MenubarRoot>
@@ -1,28 +0,0 @@
1
- import { mount, unmount } from 'svelte';
2
- import { afterEach, describe, expect, it } from 'vitest';
3
- import { configureAxe } from 'vitest-axe';
4
- import * as matchers from 'vitest-axe/matchers';
5
- import MenubarTest from './MenubarTest.svelte';
6
- expect.extend(matchers);
7
- const axe = configureAxe({
8
- rules: {
9
- region: { enabled: false },
10
- 'page-has-heading-one': { enabled: false },
11
- 'landmark-one-main': { enabled: false },
12
- },
13
- });
14
- describe('menubar a11y', () => {
15
- let cleanup;
16
- afterEach(() => cleanup?.());
17
- it('has no axe violations', async () => {
18
- const container = document.createElement('div');
19
- document.body.appendChild(container);
20
- const instance = mount(MenubarTest, { target: container });
21
- const results = await axe(container);
22
- cleanup = () => {
23
- unmount(instance);
24
- container.remove();
25
- };
26
- expect(results).toHaveNoViolations();
27
- });
28
- });
@@ -1,57 +0,0 @@
1
- import { flushSync, mount, unmount } from 'svelte';
2
- import { afterEach, describe, expect, it } from 'vitest';
3
- import MenubarTest from './MenubarTest.svelte';
4
- describe('menubar integration', () => {
5
- let target;
6
- let instance;
7
- function setup(props = {}) {
8
- target = document.createElement('div');
9
- document.body.appendChild(target);
10
- instance = mount(MenubarTest, { target, props: { portalTo: target, ...props } });
11
- flushSync();
12
- return target;
13
- }
14
- afterEach(() => {
15
- if (instance)
16
- unmount(instance);
17
- target?.remove();
18
- });
19
- it('first trigger is Tab-reachable (tabindex=0)', () => {
20
- setup();
21
- const triggers = target.querySelectorAll('[data-menubar-trigger]');
22
- expect(triggers[0]?.getAttribute('tabindex')).toBe('0');
23
- expect(triggers[1]?.getAttribute('tabindex')).toBe('-1');
24
- expect(triggers[2]?.getAttribute('tabindex')).toBe('-1');
25
- });
26
- it('full flow: click → select item → menu closes', () => {
27
- setup();
28
- const triggers = target.querySelectorAll('[data-menubar-trigger]');
29
- triggers[0].click();
30
- flushSync();
31
- const menu = target.querySelector('[role="menu"]');
32
- expect(menu).toBeTruthy();
33
- const items = menu.querySelectorAll('[role="menuitem"]');
34
- items[0].click();
35
- flushSync();
36
- expect(target.querySelector('[role="menu"]')).toBeNull();
37
- });
38
- it('full flow: click File → hover Edit → Edit opens', () => {
39
- setup();
40
- const triggers = target.querySelectorAll('[data-menubar-trigger]');
41
- triggers[0].click();
42
- flushSync();
43
- triggers[1].dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
44
- flushSync();
45
- expect(triggers[0].getAttribute('data-state')).toBe('closed');
46
- expect(triggers[1].getAttribute('data-state')).toBe('open');
47
- });
48
- it('roving tabindex updates after ArrowRight', () => {
49
- setup();
50
- const triggers = target.querySelectorAll('[data-menubar-trigger]');
51
- triggers[0].focus();
52
- triggers[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
53
- flushSync();
54
- expect(triggers[1]?.getAttribute('tabindex')).toBe('0');
55
- expect(triggers[0]?.getAttribute('tabindex')).toBe('-1');
56
- });
57
- });