@getmicdrop/svelte-components 5.17.1 → 5.17.4

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 (199) hide show
  1. package/dist/calendar/Calendar/MiniMonthCalendar.svelte +5 -7
  2. package/dist/calendar/Calendar/MiniMonthCalendar.svelte.d.ts.map +1 -1
  3. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +2 -3
  4. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte.d.ts.map +1 -1
  5. package/dist/calendar/PublicCard/PublicCard.svelte +23 -14
  6. package/dist/calendar/PublicCard/PublicCard.svelte.d.ts.map +1 -1
  7. package/dist/calendar/ShowCard/ShowCard.spec.js +1 -7
  8. package/dist/calendar/ShowCard/ShowCard.svelte +10 -1
  9. package/dist/calendar/ShowCard/ShowCard.svelte.d.ts.map +1 -1
  10. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +11 -0
  11. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte.d.ts +2 -0
  12. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte.d.ts.map +1 -1
  13. package/dist/components/Heading.spec.d.ts +2 -0
  14. package/dist/components/Heading.spec.d.ts.map +1 -0
  15. package/dist/components/Heading.spec.js +89 -0
  16. package/dist/components/Layout/__tests__/AppShell.test.js +140 -0
  17. package/dist/components/Text.spec.d.ts +2 -0
  18. package/dist/components/Text.spec.d.ts.map +1 -0
  19. package/dist/components/Text.spec.js +89 -0
  20. package/dist/config.d.ts +102 -0
  21. package/dist/config.js +147 -1
  22. package/dist/datetime/README.md +323 -0
  23. package/dist/forms/createFormStore.svelte.spec.d.ts +2 -0
  24. package/dist/forms/createFormStore.svelte.spec.d.ts.map +1 -0
  25. package/dist/forms/createFormStore.svelte.spec.js +387 -0
  26. package/dist/messages.d.ts +43 -0
  27. package/dist/messages.d.ts.map +1 -0
  28. package/dist/messages.js +57 -0
  29. package/dist/patterns/chat/ChatActivityNotice.spec.d.ts +2 -0
  30. package/dist/patterns/chat/ChatActivityNotice.spec.d.ts.map +1 -0
  31. package/dist/patterns/chat/ChatActivityNotice.spec.js +59 -0
  32. package/dist/patterns/chat/ChatBubble.spec.d.ts +2 -0
  33. package/dist/patterns/chat/ChatBubble.spec.d.ts.map +1 -0
  34. package/dist/patterns/chat/ChatBubble.spec.js +91 -0
  35. package/dist/patterns/chat/ChatContainer.spec.d.ts +2 -0
  36. package/dist/patterns/chat/ChatContainer.spec.d.ts.map +1 -0
  37. package/dist/patterns/chat/ChatContainer.spec.js +30 -0
  38. package/dist/patterns/chat/ChatDateDivider.spec.d.ts +2 -0
  39. package/dist/patterns/chat/ChatDateDivider.spec.d.ts.map +1 -0
  40. package/dist/patterns/chat/ChatDateDivider.spec.js +30 -0
  41. package/dist/patterns/chat/ChatInvitationBubble.spec.d.ts +2 -0
  42. package/dist/patterns/chat/ChatInvitationBubble.spec.d.ts.map +1 -0
  43. package/dist/patterns/chat/ChatInvitationBubble.spec.js +46 -0
  44. package/dist/patterns/chat/ChatInvitationNotice.spec.d.ts +2 -0
  45. package/dist/patterns/chat/ChatInvitationNotice.spec.d.ts.map +1 -0
  46. package/dist/patterns/chat/ChatInvitationNotice.spec.js +32 -0
  47. package/dist/patterns/chat/ChatMessageGroup.spec.d.ts +2 -0
  48. package/dist/patterns/chat/ChatMessageGroup.spec.d.ts.map +1 -0
  49. package/dist/patterns/chat/ChatMessageGroup.spec.js +58 -0
  50. package/dist/patterns/chat/ChatSlotUpdate.spec.d.ts +2 -0
  51. package/dist/patterns/chat/ChatSlotUpdate.spec.d.ts.map +1 -0
  52. package/dist/patterns/chat/ChatSlotUpdate.spec.js +65 -0
  53. package/dist/patterns/chat/ChatStatusBadge.spec.d.ts +2 -0
  54. package/dist/patterns/chat/ChatStatusBadge.spec.d.ts.map +1 -0
  55. package/dist/patterns/chat/ChatStatusBadge.spec.js +79 -0
  56. package/dist/patterns/chat/ChatStatusTransition.spec.d.ts +2 -0
  57. package/dist/patterns/chat/ChatStatusTransition.spec.d.ts.map +1 -0
  58. package/dist/patterns/chat/ChatStatusTransition.spec.js +81 -0
  59. package/dist/patterns/chat/ChatTextBubble.spec.d.ts +2 -0
  60. package/dist/patterns/chat/ChatTextBubble.spec.d.ts.map +1 -0
  61. package/dist/patterns/chat/ChatTextBubble.spec.js +35 -0
  62. package/dist/patterns/data/DataTable.spec.js +61 -0
  63. package/dist/patterns/forms/FormGrid.spec.js +34 -0
  64. package/dist/patterns/layout/Sidebar.spec.js +240 -1
  65. package/dist/patterns/layout/SidebarTestWrapper.svelte +34 -0
  66. package/dist/patterns/layout/SidebarTestWrapper.svelte.d.ts +23 -0
  67. package/dist/patterns/layout/SidebarTestWrapper.svelte.d.ts.map +1 -0
  68. package/dist/patterns/navigation/Header.spec.js +123 -0
  69. package/dist/primitives/Accordion/Accordion.spec.js +112 -2
  70. package/dist/primitives/Accordion/AccordionToggleWrapper.test.svelte +28 -0
  71. package/dist/primitives/Accordion/AccordionToggleWrapper.test.svelte.d.ts +7 -0
  72. package/dist/primitives/Accordion/AccordionToggleWrapper.test.svelte.d.ts.map +1 -0
  73. package/dist/primitives/Avatar/Avatar.spec.js +23 -0
  74. package/dist/primitives/BottomSheet/BottomSheet.spec.js +102 -0
  75. package/dist/primitives/BottomSheet/BottomSheetWithActions.test.svelte +20 -0
  76. package/dist/primitives/BottomSheet/BottomSheetWithActions.test.svelte.d.ts +10 -0
  77. package/dist/primitives/BottomSheet/BottomSheetWithActions.test.svelte.d.ts.map +1 -0
  78. package/dist/primitives/Button/ButtonGroup.spec.d.ts +2 -0
  79. package/dist/primitives/Button/ButtonGroup.spec.d.ts.map +1 -0
  80. package/dist/primitives/Button/ButtonGroup.spec.js +44 -0
  81. package/dist/primitives/Checkbox/Checkbox.spec.js +32 -0
  82. package/dist/primitives/Drawer/Drawer.spec.js +437 -0
  83. package/dist/primitives/Drawer/DrawerTestWrapper.svelte +86 -0
  84. package/dist/primitives/Drawer/DrawerTestWrapper.svelte.d.ts +26 -0
  85. package/dist/primitives/Drawer/DrawerTestWrapper.svelte.d.ts.map +1 -0
  86. package/dist/primitives/Dropdown/Dropdown.spec.js +116 -0
  87. package/dist/primitives/Dropdown/DropdownDivider.spec.d.ts +2 -0
  88. package/dist/primitives/Dropdown/DropdownDivider.spec.d.ts.map +1 -0
  89. package/dist/primitives/Dropdown/DropdownDivider.spec.js +30 -0
  90. package/dist/primitives/Dropdown/DropdownItem.spec.js +155 -1
  91. package/dist/primitives/Dropdown/DropdownItemTestWrapper.svelte +43 -0
  92. package/dist/primitives/Dropdown/DropdownItemTestWrapper.svelte.d.ts +17 -0
  93. package/dist/primitives/Dropdown/DropdownItemTestWrapper.svelte.d.ts.map +1 -0
  94. package/dist/primitives/Helper/Helper.spec.d.ts +2 -0
  95. package/dist/primitives/Helper/Helper.spec.d.ts.map +1 -0
  96. package/dist/primitives/Helper/Helper.spec.js +57 -0
  97. package/dist/primitives/Input/Input.spec.js +664 -0
  98. package/dist/primitives/Input/Input.svelte +18 -10
  99. package/dist/primitives/Input/Input.svelte.d.ts.map +1 -1
  100. package/dist/primitives/Input/Select.spec.js +414 -0
  101. package/dist/primitives/Label/Label.spec.js +9 -0
  102. package/dist/primitives/LandingButton/LandingButton.spec.d.ts +2 -0
  103. package/dist/primitives/LandingButton/LandingButton.spec.d.ts.map +1 -0
  104. package/dist/primitives/LandingButton/LandingButton.spec.js +61 -0
  105. package/dist/primitives/MenuItem/MenuItem.spec.d.ts +2 -0
  106. package/dist/primitives/MenuItem/MenuItem.spec.d.ts.map +1 -0
  107. package/dist/primitives/MenuItem/MenuItem.spec.js +130 -0
  108. package/dist/primitives/Modal/Modal.spec.js +215 -0
  109. package/dist/primitives/NavItem/NavItem.spec.d.ts +2 -0
  110. package/dist/primitives/NavItem/NavItem.spec.d.ts.map +1 -0
  111. package/dist/primitives/NavItem/NavItem.spec.js +97 -0
  112. package/dist/primitives/SearchResultItem/SearchResultItem.spec.d.ts +2 -0
  113. package/dist/primitives/SearchResultItem/SearchResultItem.spec.d.ts.map +1 -0
  114. package/dist/primitives/SearchResultItem/SearchResultItem.spec.js +78 -0
  115. package/dist/primitives/SidebarToggle/SidebarToggle.spec.d.ts +2 -0
  116. package/dist/primitives/SidebarToggle/SidebarToggle.spec.d.ts.map +1 -0
  117. package/dist/primitives/SidebarToggle/SidebarToggle.spec.js +61 -0
  118. package/dist/primitives/Spinner/Spinner.spec.js +13 -0
  119. package/dist/primitives/Toggle.spec.js +75 -0
  120. package/dist/primitives/ToggleTestWrapper.svelte +30 -0
  121. package/dist/primitives/ToggleTestWrapper.svelte.d.ts +29 -0
  122. package/dist/primitives/ToggleTestWrapper.svelte.d.ts.map +1 -0
  123. package/dist/primitives/Tooltip/Tooltip.spec.d.ts +2 -0
  124. package/dist/primitives/Tooltip/Tooltip.spec.d.ts.map +1 -0
  125. package/dist/primitives/Tooltip/Tooltip.spec.js +126 -0
  126. package/dist/recipes/inputs/Search.spec.js +66 -2
  127. package/dist/recipes/modals/ConfirmationModal.spec.js +190 -0
  128. package/dist/schemas/__tests__/auth.test.d.ts +2 -0
  129. package/dist/schemas/__tests__/auth.test.d.ts.map +1 -0
  130. package/dist/schemas/__tests__/auth.test.js +210 -0
  131. package/dist/schemas/__tests__/common.test.d.ts +2 -0
  132. package/dist/schemas/__tests__/common.test.d.ts.map +1 -0
  133. package/dist/schemas/__tests__/common.test.js +340 -0
  134. package/dist/schemas/__tests__/domain.test.d.ts +2 -0
  135. package/dist/schemas/__tests__/domain.test.d.ts.map +1 -0
  136. package/dist/schemas/__tests__/domain.test.js +293 -0
  137. package/dist/schemas/__tests__/order.test.d.ts +2 -0
  138. package/dist/schemas/__tests__/order.test.d.ts.map +1 -0
  139. package/dist/schemas/__tests__/order.test.js +349 -0
  140. package/dist/schemas/__tests__/user.test.d.ts +2 -0
  141. package/dist/schemas/__tests__/user.test.d.ts.map +1 -0
  142. package/dist/schemas/__tests__/user.test.js +325 -0
  143. package/dist/schemas/auth.d.ts +41 -0
  144. package/dist/schemas/auth.d.ts.map +1 -0
  145. package/dist/schemas/auth.js +69 -0
  146. package/dist/schemas/common.d.ts +43 -0
  147. package/dist/schemas/common.d.ts.map +1 -0
  148. package/dist/schemas/common.js +157 -0
  149. package/dist/schemas/event.d.ts +82 -0
  150. package/dist/schemas/event.d.ts.map +1 -0
  151. package/dist/schemas/event.js +58 -0
  152. package/dist/schemas/index.d.ts +10 -0
  153. package/dist/schemas/index.d.ts.map +1 -0
  154. package/dist/schemas/index.js +9 -0
  155. package/dist/schemas/order.d.ts +111 -0
  156. package/dist/schemas/order.d.ts.map +1 -0
  157. package/dist/schemas/order.js +73 -0
  158. package/dist/schemas/performer.d.ts +133 -0
  159. package/dist/schemas/performer.d.ts.map +1 -0
  160. package/dist/schemas/performer.js +73 -0
  161. package/dist/schemas/promo.d.ts +87 -0
  162. package/dist/schemas/promo.d.ts.map +1 -0
  163. package/dist/schemas/promo.js +98 -0
  164. package/dist/schemas/ticket.d.ts +104 -0
  165. package/dist/schemas/ticket.d.ts.map +1 -0
  166. package/dist/schemas/ticket.js +82 -0
  167. package/dist/schemas/user.d.ts +92 -0
  168. package/dist/schemas/user.d.ts.map +1 -0
  169. package/dist/schemas/user.js +53 -0
  170. package/dist/schemas/venue.d.ts +95 -0
  171. package/dist/schemas/venue.d.ts.map +1 -0
  172. package/dist/schemas/venue.js +52 -0
  173. package/dist/stores/auth.svelte.spec.d.ts +2 -0
  174. package/dist/stores/auth.svelte.spec.d.ts.map +1 -0
  175. package/dist/stores/auth.svelte.spec.js +112 -0
  176. package/dist/stores/formDataStore.svelte.spec.d.ts +2 -0
  177. package/dist/stores/formDataStore.svelte.spec.d.ts.map +1 -0
  178. package/dist/stores/formDataStore.svelte.spec.js +150 -0
  179. package/dist/stores/formSave.svelte.spec.d.ts +2 -0
  180. package/dist/stores/formSave.svelte.spec.d.ts.map +1 -0
  181. package/dist/stores/formSave.svelte.spec.js +196 -0
  182. package/dist/stores/navigation.spec.d.ts +2 -0
  183. package/dist/stores/navigation.spec.d.ts.map +1 -0
  184. package/dist/stores/navigation.spec.js +53 -0
  185. package/dist/telemetry.spec.js +5 -0
  186. package/dist/tokens/__tests__/sizing.test.js +2 -2
  187. package/dist/tokens/sizing.d.ts +5 -0
  188. package/dist/tokens/sizing.d.ts.map +1 -1
  189. package/dist/tokens/sizing.js +6 -0
  190. package/dist/utils/haptic.spec.d.ts +2 -0
  191. package/dist/utils/haptic.spec.d.ts.map +1 -0
  192. package/dist/utils/haptic.spec.js +153 -0
  193. package/dist/utils/imageOptimizer.spec.d.ts +2 -0
  194. package/dist/utils/imageOptimizer.spec.d.ts.map +1 -0
  195. package/dist/utils/imageOptimizer.spec.js +201 -0
  196. package/dist/utils/logger.spec.d.ts +2 -0
  197. package/dist/utils/logger.spec.d.ts.map +1 -0
  198. package/dist/utils/logger.spec.js +95 -0
  199. package/package.json +1 -2
@@ -1,6 +1,14 @@
1
- import { render } from '@testing-library/svelte';
1
+ import { render, screen } from '@testing-library/svelte';
2
+ import { createRawSnippet } from 'svelte';
2
3
  import { expect, describe, test } from 'vitest';
3
4
  import Sidebar from './Sidebar.svelte';
5
+ import SidebarTestWrapper from './SidebarTestWrapper.svelte';
6
+
7
+ function textSnippet(text) {
8
+ return createRawSnippet(() => ({
9
+ render: () => `<span>${text}</span>`
10
+ }));
11
+ }
4
12
 
5
13
  describe('Sidebar Component', () => {
6
14
  test('renders a wrapper div', () => {
@@ -157,3 +165,234 @@ describe('Sidebar Combinations', () => {
157
165
  expect(children[1]).toHaveClass('lg:w-96');
158
166
  });
159
167
  });
168
+
169
+ describe('Sidebar Snippet Rendering (lines 25-32)', () => {
170
+ test('renders sidebar snippet content when provided', () => {
171
+ render(SidebarTestWrapper, {
172
+ props: { showSidebar: true, sidebarText: 'Stats Panel' }
173
+ });
174
+
175
+ expect(screen.getByTestId('sidebar-content')).toBeInTheDocument();
176
+ expect(screen.getByTestId('sidebar-content').textContent).toBe('Stats Panel');
177
+ });
178
+
179
+ test('renders children snippet content when provided', () => {
180
+ render(SidebarTestWrapper, {
181
+ props: { showChildren: true, childrenText: 'Main Area' }
182
+ });
183
+
184
+ expect(screen.getByTestId('main-content')).toBeInTheDocument();
185
+ expect(screen.getByTestId('main-content').textContent).toBe('Main Area');
186
+ });
187
+
188
+ test('renders both sidebar and children with left position', () => {
189
+ render(SidebarTestWrapper, {
190
+ props: {
191
+ sidebarPosition: 'left',
192
+ showSidebar: true,
193
+ sidebarText: 'Left Sidebar',
194
+ showChildren: true,
195
+ childrenText: 'Page Content'
196
+ }
197
+ });
198
+
199
+ expect(screen.getByTestId('sidebar-content').textContent).toBe('Left Sidebar');
200
+ expect(screen.getByTestId('main-content').textContent).toBe('Page Content');
201
+ });
202
+
203
+ test('renders both sidebar and children with right position', () => {
204
+ render(SidebarTestWrapper, {
205
+ props: {
206
+ sidebarPosition: 'right',
207
+ showSidebar: true,
208
+ sidebarText: 'Right Sidebar',
209
+ showChildren: true,
210
+ childrenText: 'Page Body'
211
+ }
212
+ });
213
+
214
+ expect(screen.getByTestId('sidebar-content').textContent).toBe('Right Sidebar');
215
+ expect(screen.getByTestId('main-content').textContent).toBe('Page Body');
216
+ });
217
+
218
+ test('sidebar content appears inside aside element with left position', () => {
219
+ const { container } = render(SidebarTestWrapper, {
220
+ props: {
221
+ sidebarPosition: 'left',
222
+ showSidebar: true
223
+ }
224
+ });
225
+
226
+ const aside = container.querySelector('aside');
227
+ expect(aside).toBeInTheDocument();
228
+ expect(aside.querySelector('[data-testid="sidebar-content"]')).toBeInTheDocument();
229
+ });
230
+
231
+ test('children content appears inside main element with left position', () => {
232
+ const { container } = render(SidebarTestWrapper, {
233
+ props: {
234
+ sidebarPosition: 'left',
235
+ showChildren: true
236
+ }
237
+ });
238
+
239
+ const main = container.querySelector('main');
240
+ expect(main).toBeInTheDocument();
241
+ expect(main.querySelector('[data-testid="main-content"]')).toBeInTheDocument();
242
+ });
243
+
244
+ test('left position renders aside before main in DOM', () => {
245
+ const { container } = render(SidebarTestWrapper, {
246
+ props: {
247
+ sidebarPosition: 'left',
248
+ showSidebar: true,
249
+ showChildren: true
250
+ }
251
+ });
252
+
253
+ const wrapper = container.firstElementChild;
254
+ const els = wrapper.children;
255
+ expect(els[0].tagName).toBe('ASIDE');
256
+ expect(els[1].tagName).toBe('MAIN');
257
+ });
258
+
259
+ test('right position renders main before aside in DOM', () => {
260
+ const { container } = render(SidebarTestWrapper, {
261
+ props: {
262
+ sidebarPosition: 'right',
263
+ showSidebar: true,
264
+ showChildren: true
265
+ }
266
+ });
267
+
268
+ const wrapper = container.firstElementChild;
269
+ const els = wrapper.children;
270
+ expect(els[0].tagName).toBe('MAIN');
271
+ expect(els[1].tagName).toBe('ASIDE');
272
+ });
273
+ });
274
+
275
+ describe('Sidebar with Raw Snippets (lines 23, 25-32)', () => {
276
+ test('renders sidebar and children snippets directly with left position', () => {
277
+ const { container } = render(Sidebar, {
278
+ props: {
279
+ sidebarPosition: 'left',
280
+ sidebar: textSnippet('Side Panel'),
281
+ children: textSnippet('Main Panel')
282
+ }
283
+ });
284
+
285
+ const aside = container.querySelector('aside');
286
+ expect(aside).toBeInTheDocument();
287
+ expect(aside.textContent).toContain('Side Panel');
288
+
289
+ const main = container.querySelector('main');
290
+ expect(main).toBeInTheDocument();
291
+ expect(main.textContent).toContain('Main Panel');
292
+ });
293
+
294
+ test('renders sidebar and children snippets directly with right position', () => {
295
+ const { container } = render(Sidebar, {
296
+ props: {
297
+ sidebarPosition: 'right',
298
+ sidebar: textSnippet('Right Side'),
299
+ children: textSnippet('Main Body')
300
+ }
301
+ });
302
+
303
+ const aside = container.querySelector('aside');
304
+ expect(aside.textContent).toContain('Right Side');
305
+
306
+ const main = container.querySelector('main');
307
+ expect(main.textContent).toContain('Main Body');
308
+ });
309
+
310
+ test('renders with sidebar snippet only (no children)', () => {
311
+ const { container } = render(Sidebar, {
312
+ props: {
313
+ sidebar: textSnippet('Only Sidebar')
314
+ }
315
+ });
316
+
317
+ const aside = container.querySelector('aside');
318
+ expect(aside.textContent).toContain('Only Sidebar');
319
+ });
320
+
321
+ test('renders with children snippet only (no sidebar)', () => {
322
+ const { container } = render(Sidebar, {
323
+ props: {
324
+ children: textSnippet('Only Main')
325
+ }
326
+ });
327
+
328
+ const main = container.querySelector('main');
329
+ expect(main.textContent).toContain('Only Main');
330
+ });
331
+
332
+ test('renders with className interpolation', () => {
333
+ const { container } = render(Sidebar, {
334
+ props: {
335
+ class: 'test-class',
336
+ sidebar: textSnippet('Sidebar'),
337
+ children: textSnippet('Main')
338
+ }
339
+ });
340
+
341
+ const wrapper = container.firstElementChild;
342
+ expect(wrapper).toHaveClass('test-class');
343
+ expect(wrapper).toHaveClass('flex');
344
+ });
345
+
346
+ test('renders without className (empty string default)', () => {
347
+ const { container } = render(Sidebar, {
348
+ props: {
349
+ sidebarPosition: 'left',
350
+ sidebar: textSnippet('Sidebar'),
351
+ children: textSnippet('Main')
352
+ }
353
+ });
354
+
355
+ const wrapper = container.firstElementChild;
356
+ expect(wrapper).toHaveClass('flex');
357
+ expect(wrapper).toHaveClass('gap-6');
358
+ });
359
+
360
+ test('handles invalid sidebarWidth gracefully', () => {
361
+ const { container } = render(Sidebar, {
362
+ props: {
363
+ sidebarWidth: 'invalid',
364
+ sidebarPosition: 'left',
365
+ sidebar: textSnippet('Sidebar'),
366
+ children: textSnippet('Main')
367
+ }
368
+ });
369
+
370
+ // Should still render without crashing
371
+ const aside = container.querySelector('aside');
372
+ expect(aside).toBeInTheDocument();
373
+ });
374
+
375
+ test('handles invalid sidebarWidth on right position', () => {
376
+ const { container } = render(Sidebar, {
377
+ props: {
378
+ sidebarWidth: 'invalid',
379
+ sidebarPosition: 'right',
380
+ sidebar: textSnippet('Sidebar'),
381
+ children: textSnippet('Main')
382
+ }
383
+ });
384
+
385
+ const main = container.querySelector('main');
386
+ expect(main).toBeInTheDocument();
387
+ });
388
+
389
+ test('renders with undefined className (no class prop)', () => {
390
+ const { container } = render(Sidebar);
391
+
392
+ const wrapper = container.firstElementChild;
393
+ // className defaults to empty string, so the class should not contain undefined
394
+ const classAttr = wrapper.getAttribute('class');
395
+ expect(classAttr).not.toContain('undefined');
396
+ expect(classAttr).toContain('flex');
397
+ });
398
+ });
@@ -0,0 +1,34 @@
1
+ <script>
2
+ import Sidebar from "./Sidebar.svelte";
3
+
4
+ /** @type {{
5
+ sidebarWidth?: 'narrow' | 'medium' | 'wide',
6
+ sidebarPosition?: 'left' | 'right',
7
+ class?: string,
8
+ showSidebar?: boolean,
9
+ sidebarText?: string,
10
+ showChildren?: boolean,
11
+ childrenText?: string,
12
+ }} */
13
+ let {
14
+ sidebarWidth = 'medium',
15
+ sidebarPosition = 'right',
16
+ class: className = '',
17
+ showSidebar = false,
18
+ sidebarText = 'Sidebar Content',
19
+ showChildren = false,
20
+ childrenText = 'Main Content',
21
+ } = $props();
22
+ </script>
23
+
24
+ <Sidebar {sidebarWidth} {sidebarPosition} class={className}>
25
+ {#snippet sidebar()}
26
+ {#if showSidebar}
27
+ <div data-testid="sidebar-content">{sidebarText}</div>
28
+ {/if}
29
+ {/snippet}
30
+
31
+ {#if showChildren}
32
+ <div data-testid="main-content">{childrenText}</div>
33
+ {/if}
34
+ </Sidebar>
@@ -0,0 +1,23 @@
1
+ export default SidebarTestWrapper;
2
+ type SidebarTestWrapper = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<{
5
+ sidebarWidth?: "medium" | "narrow" | "wide" | undefined;
6
+ sidebarPosition?: "left" | "right" | undefined;
7
+ class?: string | undefined;
8
+ showSidebar?: boolean | undefined;
9
+ sidebarText?: string | undefined;
10
+ showChildren?: boolean | undefined;
11
+ childrenText?: string | undefined;
12
+ }>): void;
13
+ };
14
+ declare const SidebarTestWrapper: import("svelte").Component<{
15
+ sidebarWidth?: "narrow" | "medium" | "wide";
16
+ sidebarPosition?: "left" | "right";
17
+ class?: string;
18
+ showSidebar?: boolean;
19
+ sidebarText?: string;
20
+ showChildren?: boolean;
21
+ childrenText?: string;
22
+ }, {}, "">;
23
+ //# sourceMappingURL=SidebarTestWrapper.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SidebarTestWrapper.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/patterns/layout/SidebarTestWrapper.svelte.js"],"names":[],"mappings":";;;;;;;;;;;;;AAkDA;mBARmB,QAAQ,GAAG,QAAQ,GAAG,MAAM;sBACzB,MAAM,GAAG,OAAO;YAC1B,MAAM;kBACA,OAAO;kBACP,MAAM;mBACL,OAAO;mBACP,MAAM;WAEwC"}
@@ -136,6 +136,18 @@ describe('Header', () => {
136
136
  await fireEvent.click(desktopButton);
137
137
  expect(container.querySelector('div.absolute.top-\\[calc\\(100\\%\\+0\\.5rem\\)\\]')).toBeFalsy();
138
138
  });
139
+ it('navigates and closes dropdown when a dropdown link is clicked', async () => {
140
+ const { goto } = await import('$app/navigation');
141
+ const { container } = render(Header, defaultProps);
142
+ const desktopButton = container.querySelector('button.hidden.md\\:block');
143
+ await fireEvent.click(desktopButton);
144
+ // Click the "Account Settings" menu item in the dropdown
145
+ const accountSettings = screen.getByText('Account Settings');
146
+ await fireEvent.click(accountSettings);
147
+ expect(goto).toHaveBeenCalledWith('/account-settings');
148
+ // Dropdown should be closed after clicking
149
+ expect(container.querySelector('div.absolute.top-\\[calc\\(100\\%\\+0\\.5rem\\)\\]')).toBeFalsy();
150
+ });
139
151
  });
140
152
  describe('mobile sheet', () => {
141
153
  it('triggers mobile sheet state when mobile avatar is clicked', async () => {
@@ -150,6 +162,69 @@ describe('Header', () => {
150
162
  const mobileSheet = document.body.querySelector('div.fixed.inset-0.bg-black\\/50');
151
163
  expect(mobileSheet).toBeTruthy();
152
164
  });
165
+ it('navigates when a mobile sheet dropdown link is clicked', async () => {
166
+ const { goto } = await import('$app/navigation');
167
+ const { container } = render(Header, defaultProps);
168
+ const mobileButton = container.querySelector('button.block.md\\:hidden');
169
+ await fireEvent.click(mobileButton);
170
+ // Mobile sheet renders via portal in document.body
171
+ const mobileSheet = document.body.querySelector('div.fixed.inset-0.bg-black\\/50');
172
+ expect(mobileSheet).toBeTruthy();
173
+ // Click the first dropdown link in the mobile sheet
174
+ const menuItems = mobileSheet.querySelectorAll('button');
175
+ // Find the menu item with "Account Settings" text
176
+ const accountBtn = Array.from(menuItems).find(btn => btn.textContent?.includes('Account Settings'));
177
+ expect(accountBtn).toBeTruthy();
178
+ await fireEvent.click(accountBtn);
179
+ expect(goto).toHaveBeenCalledWith('/account-settings');
180
+ });
181
+ it('calls signoutHandler and closes sheet when mobile sign out is clicked', async () => {
182
+ const signoutHandler = vi.fn();
183
+ const { container } = render(Header, { ...defaultProps, signoutHandler });
184
+ const mobileButton = container.querySelector('button.block.md\\:hidden');
185
+ await fireEvent.click(mobileButton);
186
+ const mobileSheet = document.body.querySelector('div.fixed.inset-0.bg-black\\/50');
187
+ expect(mobileSheet).toBeTruthy();
188
+ // Find the Sign Out button in the mobile sheet
189
+ const menuItems = mobileSheet.querySelectorAll('button');
190
+ const signOutBtn = Array.from(menuItems).find(btn => btn.textContent?.includes('Sign Out'));
191
+ expect(signOutBtn).toBeTruthy();
192
+ await fireEvent.click(signOutBtn);
193
+ expect(signoutHandler).toHaveBeenCalled();
194
+ });
195
+ });
196
+ describe('back button mode desktop dropdown', () => {
197
+ it('shows dropdown with dropdown links and sign out when avatar clicked in back-button mode', async () => {
198
+ const { goto } = await import('$app/navigation');
199
+ const signoutHandler = vi.fn();
200
+ const { container } = render(Header, { ...defaultProps, showBackButton: true, signoutHandler });
201
+ // In back-button mode, the desktop section is within "hidden md:flex"
202
+ // The avatar button is the only AvatarButton in that section
203
+ const avatarButtons = container.querySelectorAll('button');
204
+ // Find the avatar button that opens the dropdown (not the Back button)
205
+ const desktopAvatarBtn = Array.from(avatarButtons).find(btn => !btn.textContent?.includes('Back') && btn.querySelector('img'));
206
+ expect(desktopAvatarBtn).toBeTruthy();
207
+ await fireEvent.click(desktopAvatarBtn);
208
+ // Dropdown should be visible
209
+ const dropdown = container.querySelector('div.absolute.top-\\[calc\\(100\\%\\+0\\.5rem\\)\\]');
210
+ expect(dropdown).toBeTruthy();
211
+ // Click a dropdown link
212
+ const accountSettings = screen.getByText('Account Settings');
213
+ await fireEvent.click(accountSettings);
214
+ expect(goto).toHaveBeenCalledWith('/account-settings');
215
+ });
216
+ it('calls signoutHandler when sign out clicked in back-button mode dropdown', async () => {
217
+ const signoutHandler = vi.fn();
218
+ const { container } = render(Header, { ...defaultProps, showBackButton: true, signoutHandler });
219
+ // Find the avatar button (not the Back button)
220
+ const avatarButtons = container.querySelectorAll('button');
221
+ const desktopAvatarBtn = Array.from(avatarButtons).find(btn => !btn.textContent?.includes('Back') && btn.querySelector('img'));
222
+ await fireEvent.click(desktopAvatarBtn);
223
+ // Click sign out
224
+ const signOutBtn = screen.getByText('Sign out');
225
+ await fireEvent.click(signOutBtn);
226
+ expect(signoutHandler).toHaveBeenCalled();
227
+ });
153
228
  });
154
229
  describe('navigation active state', () => {
155
230
  it('marks current path as active', () => {
@@ -166,5 +241,53 @@ describe('Header', () => {
166
241
  await fireEvent.click(desktopButton);
167
242
  expect(screen.getByText('Performer')).toBeTruthy();
168
243
  });
244
+ it('shows name in mobile sheet when provided', async () => {
245
+ const { container } = render(Header, { ...defaultProps, name: 'Jane Doe' });
246
+ const mobileButton = container.querySelector('button.block.md\\:hidden');
247
+ await fireEvent.click(mobileButton);
248
+ const mobileSheet = document.body.querySelector('div.fixed.inset-0.bg-black\\/50');
249
+ expect(mobileSheet).toBeTruthy();
250
+ expect(mobileSheet.textContent).toContain('Jane Doe');
251
+ });
252
+ it('shows "Performer" in mobile sheet when name is empty', async () => {
253
+ const { container } = render(Header, { ...defaultProps, name: '' });
254
+ const mobileButton = container.querySelector('button.block.md\\:hidden');
255
+ await fireEvent.click(mobileButton);
256
+ const mobileSheet = document.body.querySelector('div.fixed.inset-0.bg-black\\/50');
257
+ expect(mobileSheet).toBeTruthy();
258
+ expect(mobileSheet.textContent).toContain('Performer');
259
+ });
260
+ it('shows "Performer" in back-button mode dropdown when name is empty', async () => {
261
+ const { container } = render(Header, { ...defaultProps, name: '', showBackButton: true });
262
+ const avatarButtons = container.querySelectorAll('button');
263
+ const desktopAvatarBtn = Array.from(avatarButtons).find(btn => !btn.textContent?.includes('Back') && btn.querySelector('img'));
264
+ await fireEvent.click(desktopAvatarBtn);
265
+ const dropdown = container.querySelector('div.absolute.top-\\[calc\\(100\\%\\+0\\.5rem\\)\\]');
266
+ expect(dropdown).toBeTruthy();
267
+ expect(dropdown.textContent).toContain('Performer');
268
+ });
269
+ });
270
+ describe('isActive edge cases', () => {
271
+ it('marks root nav link as active when path matches base', () => {
272
+ // The mock sets page pathname to '/shows' and base to '/performers'
273
+ // Add a root link and verify it is NOT active (since path is /shows)
274
+ const propsWithRoot = {
275
+ ...defaultProps,
276
+ navLinks: [
277
+ { label: 'Home', href: '/performers/' },
278
+ ...defaultProps.navLinks
279
+ ]
280
+ };
281
+ const { container } = render(Header, propsWithRoot);
282
+ const homeLink = container.querySelector('a[href="/performers/"]');
283
+ // /shows does not match /performers/, so it should NOT be active
284
+ expect(homeLink.classList.contains('text-blue-700')).toBe(false);
285
+ });
286
+ it('marks link as active using startsWith for non-root paths', () => {
287
+ const { container } = render(Header, defaultProps);
288
+ // '/shows' starts with '/shows' so it should be active
289
+ const showsLink = container.querySelector('a[href="/shows"]');
290
+ expect(showsLink.classList.contains('text-blue-700')).toBe(true);
291
+ });
169
292
  });
170
293
  });
@@ -1,6 +1,25 @@
1
- import { render, screen } from '@testing-library/svelte';
2
- import { expect, describe, test } from 'vitest';
1
+ import { render, screen, waitFor } from '@testing-library/svelte';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { createRawSnippet } from 'svelte';
4
+ import { tick } from 'svelte';
5
+ import { expect, describe, test, vi } from 'vitest';
3
6
  import Accordion from './Accordion.svelte';
7
+ import AccordionItemWrapper from './AccordionToggleWrapper.test.svelte';
8
+
9
+ // Mock the transitions module to avoid animation issues in tests
10
+ vi.mock('../../utils/transitions.js', () => ({
11
+ safeSlide: () => ({
12
+ delay: 0,
13
+ duration: 0,
14
+ css: () => ''
15
+ })
16
+ }));
17
+
18
+ function textSnippet(text) {
19
+ return createRawSnippet(() => ({
20
+ render: () => `<span>${text}</span>`
21
+ }));
22
+ }
4
23
 
5
24
  describe('Accordion Component', () => {
6
25
  test('renders correctly with default props', () => {
@@ -66,6 +85,14 @@ describe('Accordion Component', () => {
66
85
  const accordion = container.querySelector('[data-accordion]');
67
86
  expect(accordion).toHaveClass('dark:border-gray-700');
68
87
  });
88
+
89
+ test('renders children snippet content', () => {
90
+ const { container } = render(Accordion, {
91
+ props: { children: textSnippet('Accordion body') }
92
+ });
93
+ const accordion = container.querySelector('[data-accordion]');
94
+ expect(accordion.textContent).toContain('Accordion body');
95
+ });
69
96
  });
70
97
 
71
98
  describe('Accordion Props', () => {
@@ -81,3 +108,86 @@ describe('Accordion Props', () => {
81
108
  expect(accordion).toHaveClass('border');
82
109
  });
83
110
  });
111
+
112
+ describe('Accordion toggle context (single mode)', () => {
113
+ test('opens an item when toggled', async () => {
114
+ const user = userEvent.setup();
115
+ const { container } = render(AccordionItemWrapper, {
116
+ props: { multiple: false }
117
+ });
118
+ const buttons = container.querySelectorAll('button');
119
+ expect(buttons.length).toBe(2);
120
+
121
+ // Click first item to open
122
+ await user.click(buttons[0]);
123
+ await tick();
124
+ await waitFor(() => {
125
+ expect(buttons[0]).toHaveAttribute('aria-expanded', 'true');
126
+ });
127
+ });
128
+
129
+ test('closes an item when toggled again', async () => {
130
+ const user = userEvent.setup();
131
+ const { container } = render(AccordionItemWrapper, {
132
+ props: { multiple: false }
133
+ });
134
+ const buttons = container.querySelectorAll('button');
135
+
136
+ // Open then close
137
+ await user.click(buttons[0]);
138
+ await tick();
139
+ await user.click(buttons[0]);
140
+ await tick();
141
+ await waitFor(() => {
142
+ expect(buttons[0]).toHaveAttribute('aria-expanded', 'false');
143
+ });
144
+ });
145
+
146
+ test('in single mode, opening a second item closes the first', async () => {
147
+ const user = userEvent.setup();
148
+ const { container } = render(AccordionItemWrapper, {
149
+ props: { multiple: false }
150
+ });
151
+ const buttons = container.querySelectorAll('button');
152
+
153
+ // Open first
154
+ await user.click(buttons[0]);
155
+ await tick();
156
+ await waitFor(() => {
157
+ expect(buttons[0]).toHaveAttribute('aria-expanded', 'true');
158
+ });
159
+
160
+ // Open second -- first should close
161
+ await user.click(buttons[1]);
162
+ await tick();
163
+ await waitFor(() => {
164
+ expect(buttons[0]).toHaveAttribute('aria-expanded', 'false');
165
+ expect(buttons[1]).toHaveAttribute('aria-expanded', 'true');
166
+ });
167
+ });
168
+ });
169
+
170
+ describe('Accordion toggle context (multiple mode)', () => {
171
+ test('in multiple mode, opening a second item keeps the first open', async () => {
172
+ const user = userEvent.setup();
173
+ const { container } = render(AccordionItemWrapper, {
174
+ props: { multiple: true }
175
+ });
176
+ const buttons = container.querySelectorAll('button');
177
+
178
+ // Open first
179
+ await user.click(buttons[0]);
180
+ await tick();
181
+ await waitFor(() => {
182
+ expect(buttons[0]).toHaveAttribute('aria-expanded', 'true');
183
+ });
184
+
185
+ // Open second -- first stays open
186
+ await user.click(buttons[1]);
187
+ await tick();
188
+ await waitFor(() => {
189
+ expect(buttons[0]).toHaveAttribute('aria-expanded', 'true');
190
+ expect(buttons[1]).toHaveAttribute('aria-expanded', 'true');
191
+ });
192
+ });
193
+ });
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Test wrapper for Accordion toggle context coverage.
4
+ * Renders a real Accordion with two AccordionItems so the toggle function
5
+ * and its branches (add, delete, single-mode clear) are exercised.
6
+ */
7
+ import Accordion from './Accordion.svelte';
8
+ import AccordionItem from './AccordionItem.svelte';
9
+
10
+ interface Props {
11
+ multiple?: boolean;
12
+ }
13
+
14
+ let { multiple = false }: Props = $props();
15
+ </script>
16
+
17
+ <Accordion {multiple}>
18
+ {#snippet children()}
19
+ <AccordionItem>
20
+ {#snippet header()}Item 1{/snippet}
21
+ {#snippet children()}Content 1{/snippet}
22
+ </AccordionItem>
23
+ <AccordionItem>
24
+ {#snippet header()}Item 2{/snippet}
25
+ {#snippet children()}Content 2{/snippet}
26
+ </AccordionItem>
27
+ {/snippet}
28
+ </Accordion>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ multiple?: boolean;
3
+ }
4
+ declare const AccordionToggleWrapper: import("svelte").Component<Props, {}, "">;
5
+ type AccordionToggleWrapper = ReturnType<typeof AccordionToggleWrapper>;
6
+ export default AccordionToggleWrapper;
7
+ //# sourceMappingURL=AccordionToggleWrapper.test.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccordionToggleWrapper.test.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/primitives/Accordion/AccordionToggleWrapper.test.svelte.ts"],"names":[],"mappings":"AAYE,UAAU,KAAK;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAyBH,QAAA,MAAM,sBAAsB,2CAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
@@ -183,6 +183,29 @@ describe('Avatar Size Text Classes', () => {
183
183
  });
184
184
  });
185
185
 
186
+ describe('Avatar Size Fallback', () => {
187
+ test('falls back to md size when an invalid size is provided (placeholder)', () => {
188
+ const { container } = render(Avatar, { props: { size: 'invalid-size' } });
189
+ const div = container.querySelector('div');
190
+ expect(div).toHaveClass('w-10');
191
+ expect(div).toHaveClass('h-10');
192
+ });
193
+
194
+ test('falls back to md size when an invalid size is provided (initials)', () => {
195
+ const { container } = render(Avatar, { props: { size: 'invalid-size', initials: 'XY' } });
196
+ const div = container.querySelector('div');
197
+ expect(div).toHaveClass('w-10');
198
+ expect(div).toHaveClass('h-10');
199
+ });
200
+
201
+ test('falls back to md size when an invalid size is provided (img)', () => {
202
+ const { container } = render(Avatar, { props: { size: 'invalid-size', src: '/user.jpg' } });
203
+ const img = container.querySelector('img');
204
+ expect(img).toHaveClass('w-10');
205
+ expect(img).toHaveClass('h-10');
206
+ });
207
+ });
208
+
186
209
  describe('Avatar Rounded', () => {
187
210
  test('default rounded is false (uses rounded class)', () => {
188
211
  const { container } = render(Avatar);