@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
@@ -2,6 +2,7 @@
2
2
  import { ChevronLeftOutline, ChevronRightOutline, CheckCircleSolid } from '../../primitives/Icons';
3
3
  import Spinner from '../../primitives/Spinner/Spinner.svelte';
4
4
  import { typography } from '../../tokens/typography';
5
+ import { formatDayOfWeek, formatMonth } from '../../datetime/format';
5
6
 
6
7
  let {
7
8
  variant = 'availability',
@@ -592,13 +593,10 @@
592
593
  }
593
594
 
594
595
  function getDayAriaLabel(day) {
595
- const date = new Date(currentYear, currentMonth, day.day);
596
- const dateStr = date.toLocaleDateString('en-US', {
597
- weekday: 'long',
598
- month: 'long',
599
- day: 'numeric',
600
- year: 'numeric'
601
- });
596
+ const isoStr = new Date(currentYear, currentMonth, day.day).toISOString();
597
+ const dayOfWeek = formatDayOfWeek(isoStr, 'UTC', false);
598
+ const month = formatMonth(isoStr, 'UTC', false);
599
+ const dateStr = `${dayOfWeek}, ${month} ${day.day}, ${currentYear}`;
602
600
 
603
601
  let label = dateStr;
604
602
  if (day.isToday) label += ', Today';
@@ -1 +1 @@
1
- {"version":3,"file":"MiniMonthCalendar.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/Calendar/MiniMonthCalendar.svelte.js"],"names":[],"mappings":";;;;;AAitBA;cArsB+B,MAAM;oBAAkB,GAAG,EAAE;aAAW,GAAG,EAAE;iBAAe,MAAM;iBAAe,OAAO;qBAAmB,OAAO;sBAAoB,OAAO;eAAa,OAAO;yBAAuB,OAAO;4BAA0B,OAAO;kBAAgB,GAAG;gBAAc,GAAG;mBAAiB,GAAG;wBAqsBvP;wBArsB7C;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC;IAAC,aAAa,EAAE,GAAG,CAAA;CAAE"}
1
+ {"version":3,"file":"MiniMonthCalendar.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/Calendar/MiniMonthCalendar.svelte.js"],"names":[],"mappings":";;;;;AAgtBA;cAlsB+B,MAAM;oBAAkB,GAAG,EAAE;aAAW,GAAG,EAAE;iBAAe,MAAM;iBAAe,OAAO;qBAAmB,OAAO;sBAAoB,OAAO;eAAa,OAAO;yBAAuB,OAAO;4BAA0B,OAAO;kBAAgB,GAAG;gBAAc,GAAG;mBAAiB,GAAG;wBAksBvP;wBAlsB7C;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC;IAAC,aAAa,EAAE,GAAG,CAAA;CAAE"}
@@ -1,6 +1,7 @@
1
1
  <script>
2
2
  import { ChevronLeftOutline, ChevronRightOutline } from '../../primitives/Icons';
3
3
  import { typography } from '../../tokens/typography';
4
+ import { formatMonth } from '../../datetime/format';
4
5
 
5
6
  const today = new Date();
6
7
  const todayLocalMonth = today.getMonth();
@@ -63,9 +64,7 @@
63
64
 
64
65
  <header class="w-full flex items-center justify-between select-none">
65
66
  <h2 class="{typography.h1} text-3xl">
66
- {new Intl.DateTimeFormat("en", {
67
- month: "long",
68
- }).format(new Date(currentYear, currentMonth, 1))}
67
+ {formatMonth(new Date(currentYear, currentMonth, 1).toISOString(), 'UTC', false)}
69
68
  </h2>
70
69
  <div class="flex items-center gap-2">
71
70
  <button
@@ -1 +1 @@
1
- {"version":3,"file":"MonthSwitcher.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/MonthSwitcher/MonthSwitcher.svelte.js"],"names":[],"mappings":";;;;;AA0FA;;;;;;4BA5E+L,OAAO;WA4E1I;wBA5EzC;IAAE,WAAW,CAAC,SAAwB;IAAC,YAAY,CAAC,SAAyB;IAAC,UAAU,CAAC,WAAW;IAAC,UAAU,CAAC,WAAW;IAAC,WAAW,CAAC,WAAW;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAAE"}
1
+ {"version":3,"file":"MonthSwitcher.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/MonthSwitcher/MonthSwitcher.svelte.js"],"names":[],"mappings":";;;;;AA0FA;;;;;;4BA1E+L,OAAO;WA0E1I;wBA1EzC;IAAE,WAAW,CAAC,SAAwB;IAAC,YAAY,CAAC,SAAyB;IAAC,UAAU,CAAC,WAAW;IAAC,UAAU,CAAC,WAAW;IAAC,WAAW,CAAC,WAAW;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAAE"}
@@ -1,5 +1,6 @@
1
1
  <script>
2
2
  import { typography } from '../../tokens/typography';
3
+ import { formatEventDate as formatDateTz, formatEventTime } from '../../datetime/format';
3
4
 
4
5
  let {
5
6
  events = [],
@@ -14,12 +15,6 @@
14
15
 
15
16
  let placeholder = $derived(placeholderImage || DEFAULT_PLACEHOLDER);
16
17
 
17
- let options = {
18
- weekday: "short",
19
- month: "short",
20
- day: "numeric",
21
- };
22
-
23
18
  function handleResize() {
24
19
  _isSmallScreen = window.innerWidth < 768;
25
20
  }
@@ -62,11 +57,25 @@
62
57
  return status === 'Sold out' || status === 'Selling fast' || status === 'Almost sold out';
63
58
  }
64
59
 
65
- function formatEventDate(dateStr) {
66
- if (!dateStr) return '';
67
- const [year, month, day] = dateStr.split('-').map(Number);
60
+ function formatEventDate(event) {
61
+ // Prefer timezone-aware formatting via startDateTime + timeZone
62
+ if (event.startDateTime && event.timeZone) {
63
+ return formatDateTz(event.startDateTime, event.timeZone, { includeYear: false });
64
+ }
65
+ // Fallback for legacy event.date (YYYY-MM-DD)
66
+ if (!event.date) return '';
67
+ const [year, month, day] = event.date.split('-').map(Number);
68
68
  const date = new Date(year, month - 1, day);
69
- return date.toLocaleDateString("en-US", options);
69
+ return date.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" });
70
+ }
71
+
72
+ function formatTime(event) {
73
+ if (event.startDateTime && event.timeZone) {
74
+ return formatEventTime(event.startDateTime, event.timeZone);
75
+ }
76
+ // Fallback for legacy event.timeline
77
+ if (event.timeline) return event.timeline.split("-")[0].trim();
78
+ return '';
70
79
  }
71
80
  </script>
72
81
 
@@ -80,7 +89,7 @@
80
89
  onkeydown={(e) => handleKeydown(e, event)}
81
90
  tabindex="0"
82
91
  role="button"
83
- aria-label={`${event.name}, ${event.status || 'On Sale'}, ${formatEventDate(event.date)}. Press Enter to view details.`}
92
+ aria-label={`${event.name}, ${event.status || 'On Sale'}, ${formatEventDate(event)}. Press Enter to view details.`}
84
93
  >
85
94
  <div
86
95
  class="bg-gray-100 dark:bg-gray-700 flex items-center justify-center overflow-hidden {view === 'col'
@@ -122,10 +131,10 @@
122
131
  class="{typography.textMuted} {view === 'col' ? 'text-xs' : 'text-xs sm:text-sm'}"
123
132
  >
124
133
  <span class="font-medium">
125
- {formatEventDate(event.date)}
126
- {#if event.timeline}
134
+ {formatEventDate(event)}
135
+ {#if event.startDateTime || event.timeline}
127
136
  <span class="text-gray-500 dark:text-gray-500">&#8226;</span>
128
- {event.timeline.split("-")[0].trim()}
137
+ {formatTime(event)}
129
138
  {/if}
130
139
  </span>
131
140
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"PublicCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/PublicCard/PublicCard.svelte.js"],"names":[],"mappings":";;;;;AAoIA;aA5H8B,GAAG,EAAE;WAAS,MAAM;uBAAqB,GAAG;kBAAgB,GAAG;WA4HpC;wBA5HtC;IAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,GAAG,CAAC;IAAC,YAAY,EAAE,GAAG,CAAA;CAAE"}
1
+ {"version":3,"file":"PublicCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/PublicCard/PublicCard.svelte.js"],"names":[],"mappings":";;;;;AA8IA;aApI8B,GAAG,EAAE;WAAS,MAAM;uBAAqB,GAAG;kBAAgB,GAAG;WAoIpC;wBApItC;IAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,GAAG,CAAC;IAAC,YAAY,EAAE,GAAG,CAAA;CAAE"}
@@ -281,19 +281,13 @@ describe('ShowCard Component', () => {
281
281
  });
282
282
 
283
283
  it('handles clipboard error gracefully', async () => {
284
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
285
284
  navigator.clipboard.writeText = vi.fn().mockRejectedValue(new Error('Clipboard failed'));
286
285
 
287
286
  render(ShowCard, { props: { event: mockEvent, showTitle: true } });
288
287
  const shareButton = screen.getByLabelText('Share event');
289
288
 
289
+ // Should not throw — error is silently swallowed
290
290
  await fireEvent.click(shareButton);
291
-
292
- await waitFor(() => {
293
- expect(consoleErrorSpy).toHaveBeenCalled();
294
- });
295
-
296
- consoleErrorSpy.mockRestore();
297
291
  });
298
292
 
299
293
  it('handles missing venue name in share text', async () => {
@@ -2,6 +2,7 @@
2
2
  import { ShareOutline, CheckOutline } from '../../primitives/Icons';
3
3
  import ShowTimeCard from '../ShowTimeCard/ShowTimeCard.svelte';
4
4
  import { typography } from '../../tokens/typography';
5
+ import { formatEventDateTime, formatEventTime } from '../../datetime/format';
5
6
 
6
7
  let {
7
8
  event = {},
@@ -9,6 +10,10 @@
9
10
  showTitle = true,
10
11
  formatDateTime = (dateStr) => {
11
12
  if (!dateStr) return '';
13
+ // Use timezone-aware formatting if event has timeZone
14
+ if (event.timeZone) {
15
+ return formatEventDateTime(dateStr, event.timeZone);
16
+ }
12
17
  const date = new Date(dateStr);
13
18
  if (isNaN(date.getTime())) return dateStr;
14
19
  return date.toLocaleDateString('en-US', {
@@ -22,6 +27,10 @@
22
27
  },
23
28
  formatTimeline = (timeStr) => {
24
29
  if (!timeStr) return '';
30
+ // Use timezone-aware formatting if event has timeZone
31
+ if (event.timeZone) {
32
+ return formatEventTime(timeStr, event.timeZone);
33
+ }
25
34
  const date = new Date(timeStr);
26
35
  if (!isNaN(date.getTime())) {
27
36
  return date.toLocaleTimeString('en-US', {
@@ -69,7 +78,7 @@
69
78
  shareSuccess = false;
70
79
  }, 2000);
71
80
  } catch {
72
- console.error('Share failed:', err);
81
+ // Silently fail — both share and clipboard copy failed
73
82
  }
74
83
  }
75
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ShowCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/ShowCard/ShowCard.svelte.js"],"names":[],"mappings":";;;;;AAwJA;YA5I6B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;gBAAc,GAAG,EAAE;gBAAc,OAAO;;;sBAA0E,GAAG;WA4I9G;wBA5IpC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,WAAW;IAAC,cAAc,CAAC,WAAW;IAAC,gBAAgB,EAAE,GAAG,CAAA;CAAE"}
1
+ {"version":3,"file":"ShowCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/ShowCard/ShowCard.svelte.js"],"names":[],"mappings":";;;;;AAkKA;YApJ6B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;gBAAc,GAAG,EAAE;gBAAc,OAAO;;;sBAA0E,GAAG;WAoJ9G;wBApJpC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,WAAW;IAAC,cAAc,CAAC,WAAW;IAAC,gBAAgB,EAAE,GAAG,CAAA;CAAE"}
@@ -1,10 +1,12 @@
1
1
  <script>
2
2
  import { typography } from '../../tokens/typography';
3
+ import { formatEventTime, formatEventDate } from '../../datetime/format';
3
4
 
4
5
  let {
5
6
  day = '',
6
7
  date = '',
7
8
  time = '',
9
+ timezone = '',
8
10
  isSelected = false,
9
11
  onselect,
10
12
  } = $props();
@@ -22,6 +24,11 @@
22
24
 
23
25
  function formatHour(timeStr) {
24
26
  if (!timeStr) return '';
27
+ // If we have an ISO string and timezone, use timezone-aware formatting
28
+ if (timezone && timeStr.includes('T')) {
29
+ return formatEventTime(timeStr, timezone);
30
+ }
31
+ // Fallback for HH:mm time strings
25
32
  const d = new Date(`1970-01-01T${timeStr}`);
26
33
  if (isNaN(d.getTime())) return timeStr;
27
34
  return d.toLocaleTimeString('en-US', {
@@ -33,6 +40,10 @@
33
40
 
34
41
  function formatDate(dateStr) {
35
42
  if (!dateStr) return '';
43
+ // If we have timezone, use timezone-aware formatting
44
+ if (timezone) {
45
+ return formatEventDate(dateStr, timezone, { includeYear: false });
46
+ }
36
47
  const d = new Date(dateStr);
37
48
  if (isNaN(d.getTime())) return dateStr;
38
49
  return d.toLocaleDateString('en-US', {
@@ -7,6 +7,7 @@ declare const ShowTimeCard: import("svelte").Component<{
7
7
  day?: string;
8
8
  date?: string;
9
9
  time?: string;
10
+ timezone?: string;
10
11
  isSelected?: boolean;
11
12
  onselect: any;
12
13
  }, {}, "">;
@@ -14,6 +15,7 @@ type $$ComponentProps = {
14
15
  day?: string;
15
16
  date?: string;
16
17
  time?: string;
18
+ timezone?: string;
17
19
  isSelected?: boolean;
18
20
  onselect: any;
19
21
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ShowTimeCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/ShowTimeCard/ShowTimeCard.svelte.js"],"names":[],"mappings":";;;;;AA6DA;UArD2B,MAAM;WAAS,MAAM;WAAS,MAAM;iBAAe,OAAO;cAAY,GAAG;WAqDzC;wBArDxC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE"}
1
+ {"version":3,"file":"ShowTimeCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/calendar/ShowTimeCard/ShowTimeCard.svelte.js"],"names":[],"mappings":";;;;;AAyEA;UA/D2B,MAAM;WAAS,MAAM;WAAS,MAAM;eAAa,MAAM;iBAAe,OAAO;cAAY,GAAG;WA+D5D;wBA/DxC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Heading.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Heading.spec.d.ts","sourceRoot":"","sources":["../../src/lib/components/Heading.spec.js"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { render, screen } from '@testing-library/svelte';
2
+ import { createRawSnippet } from 'svelte';
3
+ import { expect, describe, test } from 'vitest';
4
+ import Heading from './Heading.svelte';
5
+
6
+ function textSnippet(text) {
7
+ return createRawSnippet(() => ({
8
+ render: () => `<span>${text}</span>`
9
+ }));
10
+ }
11
+
12
+ describe('Heading', () => {
13
+ test('renders h2 by default', () => {
14
+ const { container } = render(Heading, { props: { children: textSnippet('Title') } });
15
+ expect(container.querySelector('h2')).toBeInTheDocument();
16
+ expect(container.textContent).toContain('Title');
17
+ });
18
+
19
+ test('renders h1 when level=1', () => {
20
+ const { container } = render(Heading, { props: { level: 1, children: textSnippet('H1') } });
21
+ expect(container.querySelector('h1')).toBeInTheDocument();
22
+ });
23
+
24
+ test('renders h3 when level=3', () => {
25
+ const { container } = render(Heading, { props: { level: 3, children: textSnippet('H3') } });
26
+ expect(container.querySelector('h3')).toBeInTheDocument();
27
+ });
28
+
29
+ test('renders h4 when level=4', () => {
30
+ const { container } = render(Heading, { props: { level: 4, children: textSnippet('H4') } });
31
+ expect(container.querySelector('h4')).toBeInTheDocument();
32
+ });
33
+
34
+ test('renders h5 when level=5', () => {
35
+ const { container } = render(Heading, { props: { level: 5, children: textSnippet('H5') } });
36
+ expect(container.querySelector('h5')).toBeInTheDocument();
37
+ });
38
+
39
+ test('renders h6 when level=6', () => {
40
+ const { container } = render(Heading, { props: { level: 6, children: textSnippet('H6') } });
41
+ expect(container.querySelector('h6')).toBeInTheDocument();
42
+ });
43
+
44
+ test('default size is lg', () => {
45
+ const { container } = render(Heading, { props: { children: textSnippet('Test') } });
46
+ expect(container.querySelector('h2')).toHaveClass('text-lg');
47
+ });
48
+
49
+ test('size xs', () => {
50
+ const { container } = render(Heading, { props: { size: 'xs', children: textSnippet('T') } });
51
+ expect(container.querySelector('h2')).toHaveClass('text-xs');
52
+ });
53
+
54
+ test('size 3xl', () => {
55
+ const { container } = render(Heading, { props: { size: '3xl', children: textSnippet('T') } });
56
+ expect(container.querySelector('h2')).toHaveClass('text-3xl');
57
+ });
58
+
59
+ test('weight bold', () => {
60
+ const { container } = render(Heading, { props: { weight: 'bold', children: textSnippet('T') } });
61
+ expect(container.querySelector('h2')).toHaveClass('font-bold');
62
+ });
63
+
64
+ test('weight semibold', () => {
65
+ const { container } = render(Heading, { props: { weight: 'semibold', children: textSnippet('T') } });
66
+ expect(container.querySelector('h2')).toHaveClass('font-semibold');
67
+ });
68
+
69
+ test('no weight class when not provided', () => {
70
+ const { container } = render(Heading, { props: { children: textSnippet('T') } });
71
+ const h2 = container.querySelector('h2');
72
+ expect(h2.className).not.toContain('font-');
73
+ });
74
+
75
+ test('passes id prop', () => {
76
+ const { container } = render(Heading, { props: { id: 'my-heading', children: textSnippet('T') } });
77
+ expect(container.querySelector('#my-heading')).toBeInTheDocument();
78
+ });
79
+
80
+ test('applies custom className', () => {
81
+ const { container } = render(Heading, { props: { class: 'custom', children: textSnippet('T') } });
82
+ expect(container.querySelector('h2')).toHaveClass('custom');
83
+ });
84
+
85
+ test('has dark mode text class', () => {
86
+ const { container } = render(Heading, { props: { children: textSnippet('T') } });
87
+ expect(container.querySelector('h2').className).toContain('dark:text-white');
88
+ });
89
+ });
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { render } from '@testing-library/svelte';
3
+ import { createRawSnippet } from 'svelte';
3
4
  import AppShell from '../AppShell.svelte';
4
5
  describe('AppShell', () => {
5
6
  describe('basic rendering', () => {
@@ -92,4 +93,143 @@ describe('AppShell', () => {
92
93
  expect(container.querySelector('nav')).toBeFalsy();
93
94
  });
94
95
  });
96
+ describe('header snippet rendering', () => {
97
+ it('renders header element when header snippet is provided', () => {
98
+ const header = createRawSnippet(() => ({
99
+ render: () => `<div>Header Content</div>`
100
+ }));
101
+ const { container } = render(AppShell, { header });
102
+ const headerEl = container.querySelector('header');
103
+ expect(headerEl).toBeTruthy();
104
+ expect(headerEl?.textContent).toContain('Header Content');
105
+ });
106
+ it('applies fixed positioning classes when fixedHeader=true and header provided', () => {
107
+ const header = createRawSnippet(() => ({
108
+ render: () => `<div>Fixed Header</div>`
109
+ }));
110
+ const { container } = render(AppShell, { header, fixedHeader: true });
111
+ const headerEl = container.querySelector('header');
112
+ expect(headerEl?.classList.contains('fixed')).toBe(true);
113
+ expect(headerEl?.classList.contains('top-0')).toBe(true);
114
+ expect(headerEl?.classList.contains('left-0')).toBe(true);
115
+ });
116
+ it('does not apply fixed positioning when fixedHeader=false and header provided', () => {
117
+ const header = createRawSnippet(() => ({
118
+ render: () => `<div>Non-fixed Header</div>`
119
+ }));
120
+ const { container } = render(AppShell, { header, fixedHeader: false });
121
+ const headerEl = container.querySelector('header');
122
+ expect(headerEl?.classList.contains('fixed')).toBe(false);
123
+ expect(headerEl?.classList.contains('top-0')).toBe(false);
124
+ expect(headerEl?.classList.contains('left-0')).toBe(false);
125
+ });
126
+ it('header has z-30 and w-full classes', () => {
127
+ const header = createRawSnippet(() => ({
128
+ render: () => `<div>Header</div>`
129
+ }));
130
+ const { container } = render(AppShell, { header });
131
+ const headerEl = container.querySelector('header');
132
+ expect(headerEl?.classList.contains('w-full')).toBe(true);
133
+ expect(headerEl?.classList.contains('z-30')).toBe(true);
134
+ });
135
+ });
136
+ describe('sidebar snippet rendering', () => {
137
+ it('renders aside element when sidebar snippet is provided', () => {
138
+ const sidebar = createRawSnippet(() => ({
139
+ render: () => `<div>Sidebar Content</div>`
140
+ }));
141
+ const { container } = render(AppShell, { sidebar });
142
+ const aside = container.querySelector('aside');
143
+ expect(aside).toBeTruthy();
144
+ expect(aside?.textContent).toContain('Sidebar Content');
145
+ });
146
+ it('applies w-64 when sidebar provided and sidebarCollapsed=false', () => {
147
+ const sidebar = createRawSnippet(() => ({
148
+ render: () => `<div>Sidebar</div>`
149
+ }));
150
+ const { container } = render(AppShell, { sidebar, sidebarCollapsed: false });
151
+ const aside = container.querySelector('aside');
152
+ expect(aside?.classList.contains('w-64')).toBe(true);
153
+ expect(aside?.classList.contains('w-20')).toBe(false);
154
+ });
155
+ it('applies w-20 when sidebar provided and sidebarCollapsed=true', () => {
156
+ const sidebar = createRawSnippet(() => ({
157
+ render: () => `<div>Sidebar</div>`
158
+ }));
159
+ const { container } = render(AppShell, { sidebar, sidebarCollapsed: true });
160
+ const aside = container.querySelector('aside');
161
+ expect(aside?.classList.contains('w-20')).toBe(true);
162
+ expect(aside?.classList.contains('w-64')).toBe(false);
163
+ });
164
+ it('applies md:ml-64 to main when sidebar provided and not collapsed', () => {
165
+ const sidebar = createRawSnippet(() => ({
166
+ render: () => `<div>Sidebar</div>`
167
+ }));
168
+ const { container } = render(AppShell, { sidebar, sidebarCollapsed: false });
169
+ const main = container.querySelector('main');
170
+ expect(main?.classList.contains('md:ml-64')).toBe(true);
171
+ });
172
+ it('applies md:ml-20 to main when sidebar provided and collapsed', () => {
173
+ const sidebar = createRawSnippet(() => ({
174
+ render: () => `<div>Sidebar</div>`
175
+ }));
176
+ const { container } = render(AppShell, { sidebar, sidebarCollapsed: true });
177
+ const main = container.querySelector('main');
178
+ expect(main?.classList.contains('md:ml-20')).toBe(true);
179
+ });
180
+ });
181
+ describe('bottomNav snippet rendering', () => {
182
+ it('renders nav element when bottomNav snippet and showBottomNav=true', () => {
183
+ const bottomNav = createRawSnippet(() => ({
184
+ render: () => `<div>Bottom Nav</div>`
185
+ }));
186
+ const { container } = render(AppShell, { bottomNav, showBottomNav: true });
187
+ const nav = container.querySelector('nav');
188
+ expect(nav).toBeTruthy();
189
+ expect(nav?.textContent).toContain('Bottom Nav');
190
+ });
191
+ it('does not render nav when bottomNav provided but showBottomNav=false', () => {
192
+ const bottomNav = createRawSnippet(() => ({
193
+ render: () => `<div>Bottom Nav</div>`
194
+ }));
195
+ const { container } = render(AppShell, { bottomNav, showBottomNav: false });
196
+ expect(container.querySelector('nav')).toBeFalsy();
197
+ });
198
+ it('applies pb-16 to main when bottomNav and showBottomNav are set', () => {
199
+ const bottomNav = createRawSnippet(() => ({
200
+ render: () => `<div>Bottom Nav</div>`
201
+ }));
202
+ const { container } = render(AppShell, { bottomNav, showBottomNav: true });
203
+ const main = container.querySelector('main');
204
+ expect(main?.classList.contains('pb-16')).toBe(true);
205
+ });
206
+ it('nav has fixed bottom positioning and z-30', () => {
207
+ const bottomNav = createRawSnippet(() => ({
208
+ render: () => `<div>Bottom Nav</div>`
209
+ }));
210
+ const { container } = render(AppShell, { bottomNav, showBottomNav: true });
211
+ const nav = container.querySelector('nav');
212
+ expect(nav?.classList.contains('fixed')).toBe(true);
213
+ expect(nav?.classList.contains('bottom-0')).toBe(true);
214
+ expect(nav?.classList.contains('z-30')).toBe(true);
215
+ });
216
+ });
217
+ describe('children snippet rendering', () => {
218
+ it('renders children content in main area', () => {
219
+ const children = createRawSnippet(() => ({
220
+ render: () => `<div>Main Content</div>`
221
+ }));
222
+ const { container } = render(AppShell, { children });
223
+ const main = container.querySelector('main');
224
+ expect(main?.textContent).toContain('Main Content');
225
+ });
226
+ it('does not render children placeholder when no children provided', () => {
227
+ const { container } = render(AppShell);
228
+ const main = container.querySelector('main');
229
+ const innerDiv = main?.querySelector('div');
230
+ expect(innerDiv).toBeTruthy();
231
+ // Inner div should be empty when no children
232
+ expect(innerDiv?.children.length).toBe(0);
233
+ });
234
+ });
95
235
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Text.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Text.spec.d.ts","sourceRoot":"","sources":["../../src/lib/components/Text.spec.js"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { render } from '@testing-library/svelte';
2
+ import { createRawSnippet } from 'svelte';
3
+ import { expect, describe, test } from 'vitest';
4
+ import Text from './Text.svelte';
5
+
6
+ function textSnippet(text) {
7
+ return createRawSnippet(() => ({
8
+ render: () => `<span>${text}</span>`
9
+ }));
10
+ }
11
+
12
+ describe('Text', () => {
13
+ test('renders a span element', () => {
14
+ const { container } = render(Text, { props: { children: textSnippet('Hello') } });
15
+ expect(container.querySelector('span')).toBeInTheDocument();
16
+ expect(container.textContent).toContain('Hello');
17
+ });
18
+
19
+ test('default size is md (text-base)', () => {
20
+ const { container } = render(Text, { props: { children: textSnippet('T') } });
21
+ expect(container.querySelector('span')).toHaveClass('text-base');
22
+ });
23
+
24
+ test('size xs', () => {
25
+ const { container } = render(Text, { props: { size: 'xs', children: textSnippet('T') } });
26
+ expect(container.querySelector('span')).toHaveClass('text-xs');
27
+ });
28
+
29
+ test('size lg', () => {
30
+ const { container } = render(Text, { props: { size: 'lg', children: textSnippet('T') } });
31
+ expect(container.querySelector('span')).toHaveClass('text-lg');
32
+ });
33
+
34
+ test('size 2xl', () => {
35
+ const { container } = render(Text, { props: { size: '2xl', children: textSnippet('T') } });
36
+ expect(container.querySelector('span')).toHaveClass('text-2xl');
37
+ });
38
+
39
+ test('default color is default (gray-900)', () => {
40
+ const { container } = render(Text, { props: { children: textSnippet('T') } });
41
+ expect(container.querySelector('span').className).toContain('text-gray-900');
42
+ });
43
+
44
+ test('color muted', () => {
45
+ const { container } = render(Text, { props: { color: 'muted', children: textSnippet('T') } });
46
+ expect(container.querySelector('span').className).toContain('text-gray-500');
47
+ });
48
+
49
+ test('color success', () => {
50
+ const { container } = render(Text, { props: { color: 'success', children: textSnippet('T') } });
51
+ expect(container.querySelector('span').className).toContain('text-green-600');
52
+ });
53
+
54
+ test('color error', () => {
55
+ const { container } = render(Text, { props: { color: 'error', children: textSnippet('T') } });
56
+ expect(container.querySelector('span').className).toContain('text-red-600');
57
+ });
58
+
59
+ test('leading tight', () => {
60
+ const { container } = render(Text, { props: { leading: 'tight', children: textSnippet('T') } });
61
+ expect(container.querySelector('span')).toHaveClass('leading-tight');
62
+ });
63
+
64
+ test('leading relaxed', () => {
65
+ const { container } = render(Text, { props: { leading: 'relaxed', children: textSnippet('T') } });
66
+ expect(container.querySelector('span')).toHaveClass('leading-relaxed');
67
+ });
68
+
69
+ test('no leading class when not provided', () => {
70
+ const { container } = render(Text, { props: { children: textSnippet('T') } });
71
+ const cls = container.querySelector('span').className;
72
+ expect(cls).not.toContain('leading-');
73
+ });
74
+
75
+ test('applies custom className', () => {
76
+ const { container } = render(Text, { props: { class: 'custom-text', children: textSnippet('T') } });
77
+ expect(container.querySelector('span')).toHaveClass('custom-text');
78
+ });
79
+
80
+ test('invalid size falls back to text-base', () => {
81
+ const { container } = render(Text, { props: { size: 'invalid', children: textSnippet('T') } });
82
+ expect(container.querySelector('span')).toHaveClass('text-base');
83
+ });
84
+
85
+ test('invalid color falls back gracefully', () => {
86
+ const { container } = render(Text, { props: { color: 'invalid', children: textSnippet('T') } });
87
+ expect(container.querySelector('span')).toBeInTheDocument();
88
+ });
89
+ });