@getmicdrop/svelte-components 5.10.0 → 5.10.3

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 (233) hide show
  1. package/dist/calendar/AboutShow/AboutShow.svelte +172 -172
  2. package/dist/calendar/Calendar/MiniMonthCalendar.svelte +782 -782
  3. package/dist/calendar/FAQs/FAQs.svelte +75 -75
  4. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +126 -126
  5. package/dist/calendar/OrderSummary/OrderSummary.svelte +367 -367
  6. package/dist/calendar/PublicCard/PublicCard.svelte +146 -146
  7. package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
  8. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
  9. package/dist/components/Heading.svelte +60 -60
  10. package/dist/components/Layout/AppShell.svelte +104 -104
  11. package/dist/components/Layout/ContentSection.svelte +80 -80
  12. package/dist/components/Layout/Grid.svelte +4 -4
  13. package/dist/components/Layout/Heading.svelte +81 -81
  14. package/dist/components/Layout/PageContainer.svelte +69 -69
  15. package/dist/components/Layout/Responsive.svelte +75 -75
  16. package/dist/components/Layout/Section.svelte +80 -80
  17. package/dist/components/Layout/ShowOnDesktop.svelte +37 -37
  18. package/dist/components/Layout/ShowOnMobile.svelte +37 -37
  19. package/dist/components/Layout/Sidebar.svelte +108 -108
  20. package/dist/components/Layout/Stack.spec.js +1 -1
  21. package/dist/components/Layout/Stack.svelte +6 -6
  22. package/dist/components/Layout/Text.svelte +87 -87
  23. package/dist/components/Layout/TwoColumn.svelte +108 -108
  24. package/dist/components/Text.svelte +53 -53
  25. package/dist/constants/validation.js +91 -91
  26. package/dist/constants/validation.spec.js +64 -64
  27. package/dist/datetime/__tests__/format.test.js +1 -1
  28. package/dist/datetime/__tests__/parse.test.js +1 -1
  29. package/dist/datetime/__tests__/timezone.test.js +1 -1
  30. package/dist/datetime/parse.js +1 -1
  31. package/dist/forms/createFormStore.svelte.js +1 -0
  32. package/dist/index.js +51 -51
  33. package/dist/patterns/data/DataGrid.svelte +45 -45
  34. package/dist/patterns/data/DataList.svelte +24 -24
  35. package/dist/patterns/data/DataTable.svelte +36 -36
  36. package/dist/patterns/forms/FormActions.spec.js +95 -95
  37. package/dist/patterns/forms/FormActions.stories.svelte +97 -97
  38. package/dist/patterns/forms/FormActions.svelte +46 -46
  39. package/dist/patterns/forms/FormGrid.svelte +33 -33
  40. package/dist/patterns/forms/FormSection.svelte +32 -32
  41. package/dist/patterns/forms/FormValidationSummary.stories.svelte +83 -83
  42. package/dist/patterns/forms/FormValidationSummary.svelte +74 -74
  43. package/dist/patterns/layout/Sidebar.svelte +39 -39
  44. package/dist/patterns/layout/index.js +29 -29
  45. package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
  46. package/dist/patterns/navigation/BottomNav.svelte +74 -74
  47. package/dist/patterns/navigation/Header.stories.svelte +77 -77
  48. package/dist/patterns/navigation/Header.svelte +255 -255
  49. package/dist/patterns/page/PageHeader.svelte +18 -18
  50. package/dist/patterns/page/PageLayout.svelte +40 -40
  51. package/dist/patterns/page/PageLoader.spec.js +57 -57
  52. package/dist/patterns/page/PageLoader.stories.svelte +137 -137
  53. package/dist/patterns/page/PageLoader.svelte +24 -24
  54. package/dist/patterns/page/SectionHeader.svelte +29 -29
  55. package/dist/presets/badges.js +112 -112
  56. package/dist/presets/buttons.js +76 -76
  57. package/dist/presets/index.js +9 -9
  58. package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
  59. package/dist/primitives/Accordion/Accordion.svelte +42 -42
  60. package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
  61. package/dist/primitives/Alert/Alert.spec.js +173 -173
  62. package/dist/primitives/Alert/Alert.stories.svelte +88 -88
  63. package/dist/primitives/Alert/Alert.svelte +27 -27
  64. package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
  65. package/dist/primitives/Avatar/Avatar.svelte +66 -66
  66. package/dist/primitives/Badges/Badge.spec.js +144 -144
  67. package/dist/primitives/Badges/Badge.stories.svelte +86 -86
  68. package/dist/primitives/Badges/Badge.svelte +99 -99
  69. package/dist/primitives/BottomSheet/BottomSheet.spec.js +136 -136
  70. package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
  71. package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
  72. package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +122 -122
  73. package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
  74. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +98 -98
  75. package/dist/primitives/Button/Button.spec.js +223 -223
  76. package/dist/primitives/Button/Button.stories.svelte +76 -76
  77. package/dist/primitives/Button/Button.svelte +283 -283
  78. package/dist/primitives/Button/ButtonGroup.svelte +50 -50
  79. package/dist/primitives/Button/ButtonSaveDemo.spec.js +146 -146
  80. package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
  81. package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
  82. package/dist/primitives/Card.spec.js +49 -49
  83. package/dist/primitives/Card.stories.svelte +22 -22
  84. package/dist/primitives/Card.svelte +28 -28
  85. package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
  86. package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
  87. package/dist/primitives/DarkModeToggle.spec.js +390 -390
  88. package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
  89. package/dist/primitives/DarkModeToggle.svelte +136 -136
  90. package/dist/primitives/Drawer/Drawer.stories.svelte +80 -80
  91. package/dist/primitives/Drawer/Drawer.svelte +120 -120
  92. package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
  93. package/dist/primitives/Dropdown/Dropdown.svelte +170 -170
  94. package/dist/primitives/Dropdown/DropdownDivider.svelte +9 -9
  95. package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
  96. package/dist/primitives/Helper/Helper.svelte +33 -33
  97. package/dist/primitives/Icons/ArrowLeft.svelte +8 -8
  98. package/dist/primitives/Icons/ArrowRight.svelte +8 -8
  99. package/dist/primitives/Icons/Availability.svelte +14 -14
  100. package/dist/primitives/Icons/Back.svelte +14 -14
  101. package/dist/primitives/Icons/CheckCircle.svelte +6 -6
  102. package/dist/primitives/Icons/CheckCircleOutline.svelte +15 -15
  103. package/dist/primitives/Icons/ChevronLeft.svelte +4 -4
  104. package/dist/primitives/Icons/ChevronRight.svelte +4 -4
  105. package/dist/primitives/Icons/Copy.svelte +15 -15
  106. package/dist/primitives/Icons/Cross.svelte +5 -5
  107. package/dist/primitives/Icons/DownArrow.svelte +8 -8
  108. package/dist/primitives/Icons/ErrorCircle.svelte +6 -6
  109. package/dist/primitives/Icons/FacebookIcon.svelte +2 -2
  110. package/dist/primitives/Icons/Home.svelte +15 -15
  111. package/dist/primitives/Icons/Icon.spec.js +169 -169
  112. package/dist/primitives/Icons/Icon.stories.svelte +100 -100
  113. package/dist/primitives/Icons/Icon.svelte +52 -52
  114. package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
  115. package/dist/primitives/Icons/Info.svelte +7 -7
  116. package/dist/primitives/Icons/InstagramIcon.svelte +4 -4
  117. package/dist/primitives/Icons/LogoInstagram.svelte +2 -2
  118. package/dist/primitives/Icons/Message.svelte +15 -15
  119. package/dist/primitives/Icons/MoonIcon.svelte +5 -5
  120. package/dist/primitives/Icons/More.svelte +21 -21
  121. package/dist/primitives/Icons/MoreHori.spec.js +61 -61
  122. package/dist/primitives/Icons/MoreHori.svelte +22 -22
  123. package/dist/primitives/Icons/Notification.svelte +14 -14
  124. package/dist/primitives/Icons/Payment.svelte +14 -14
  125. package/dist/primitives/Icons/Profile.svelte +21 -21
  126. package/dist/primitives/Icons/Reload.svelte +29 -29
  127. package/dist/primitives/Icons/Shows.svelte +21 -21
  128. package/dist/primitives/Icons/Signout.svelte +21 -21
  129. package/dist/primitives/Icons/SunIcon.svelte +8 -8
  130. package/dist/primitives/Icons/TiktokIcon.svelte +2 -2
  131. package/dist/primitives/Icons/TwitterIcon.svelte +2 -2
  132. package/dist/primitives/Icons/WarningIcon.spec.js +18 -18
  133. package/dist/primitives/Icons/WarningIcon.svelte +5 -5
  134. package/dist/primitives/Input/Input.spec.js +573 -573
  135. package/dist/primitives/Input/Input.stories.svelte +139 -139
  136. package/dist/primitives/Input/Input.svelte +423 -423
  137. package/dist/primitives/Input/Select.spec.js +218 -218
  138. package/dist/primitives/Input/Select.stories.svelte +112 -112
  139. package/dist/primitives/Input/Select.svelte +252 -252
  140. package/dist/primitives/Input/Textarea.stories.svelte +137 -137
  141. package/dist/primitives/Input/Textarea.svelte +105 -105
  142. package/dist/primitives/Label/Label.svelte +37 -37
  143. package/dist/primitives/Modal/Modal.spec.js +99 -99
  144. package/dist/primitives/Modal/Modal.stories.svelte +86 -86
  145. package/dist/primitives/Modal/Modal.svelte +157 -157
  146. package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
  147. package/dist/primitives/Pagination/Pagination.svelte +261 -261
  148. package/dist/primitives/Radio/Radio.stories.svelte +80 -80
  149. package/dist/primitives/Radio/Radio.svelte +67 -67
  150. package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
  151. package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
  152. package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
  153. package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
  154. package/dist/primitives/Skeleton/Skeleton.svelte +26 -26
  155. package/dist/primitives/Spinner/Spinner.spec.js +71 -71
  156. package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
  157. package/dist/primitives/Spinner/Spinner.svelte +20 -20
  158. package/dist/primitives/Tabs/TabItem.svelte +49 -49
  159. package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
  160. package/dist/primitives/Tabs/Tabs.svelte +137 -137
  161. package/dist/primitives/Toggle.spec.js +146 -146
  162. package/dist/primitives/Toggle.stories.svelte +92 -92
  163. package/dist/primitives/Toggle.svelte +131 -132
  164. package/dist/primitives/Tooltip/Tooltip.svelte +83 -83
  165. package/dist/primitives/Typography/Typography.svelte +53 -53
  166. package/dist/primitives/ValidationError.spec.js +103 -103
  167. package/dist/primitives/ValidationError.stories.svelte +69 -69
  168. package/dist/primitives/ValidationError.svelte +29 -29
  169. package/dist/primitives/index.js +92 -92
  170. package/dist/recipes/CropImage/CropImage.spec.js +208 -208
  171. package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
  172. package/dist/recipes/CropImage/CropImage.svelte +219 -219
  173. package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
  174. package/dist/recipes/ImageUploader/ImageUploader.svelte +970 -970
  175. package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
  176. package/dist/recipes/feedback/EmptyState/EmptyState.svelte +1 -1
  177. package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
  178. package/dist/recipes/feedback/ErrorDisplay.stories.svelte +101 -101
  179. package/dist/recipes/feedback/ErrorDisplay.svelte +1 -1
  180. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +133 -133
  181. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +157 -157
  182. package/dist/recipes/fields/CheckboxField.svelte +85 -85
  183. package/dist/recipes/fields/FormField.svelte +58 -58
  184. package/dist/recipes/fields/RadioGroup.svelte +95 -95
  185. package/dist/recipes/fields/SelectField.svelte +80 -80
  186. package/dist/recipes/fields/TextareaField.svelte +97 -97
  187. package/dist/recipes/fields/ToggleField.svelte +60 -60
  188. package/dist/recipes/fields/index.js +7 -7
  189. package/dist/recipes/inputs/MultiSelect.spec.js +260 -260
  190. package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
  191. package/dist/recipes/inputs/MultiSelect.svelte +283 -283
  192. package/dist/recipes/inputs/OTPInput.spec.js +251 -251
  193. package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
  194. package/dist/recipes/inputs/OTPInput.svelte +117 -117
  195. package/dist/recipes/inputs/PasswordInput.svelte +22 -22
  196. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +131 -117
  197. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte.d.ts.map +1 -1
  198. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +123 -123
  199. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +344 -344
  200. package/dist/recipes/inputs/Search.svelte +102 -102
  201. package/dist/recipes/inputs/SelectDropdown.svelte +171 -171
  202. package/dist/recipes/modals/AlertModal.svelte +130 -130
  203. package/dist/recipes/modals/ConfirmationModal.spec.js +206 -206
  204. package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
  205. package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
  206. package/dist/recipes/modals/InputModal.svelte +182 -182
  207. package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
  208. package/dist/recipes/modals/ModalStateManager.svelte +77 -77
  209. package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
  210. package/dist/recipes/modals/StatusModal.svelte +206 -206
  211. package/dist/services/EventService.js +75 -75
  212. package/dist/services/EventService.spec.js +217 -217
  213. package/dist/services/ShowService.spec.js +345 -345
  214. package/dist/stores/toaster.js +13 -13
  215. package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
  216. package/dist/stories/ButtonAuditReview.svelte +427 -427
  217. package/dist/stories/PatternsGallery.stories.svelte +19 -19
  218. package/dist/stories/PatternsGallery.svelte +206 -206
  219. package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
  220. package/dist/stories/PrimitivesGallery.svelte +752 -752
  221. package/dist/stories/RecipesGallery.stories.svelte +19 -19
  222. package/dist/stories/RecipesGallery.svelte +471 -471
  223. package/dist/stories/button-audit-manifest.json +11186 -11186
  224. package/dist/tailwind/preset.cjs +82 -82
  225. package/dist/telemetry.js +405 -405
  226. package/dist/telemetry.spec.js +1169 -1169
  227. package/dist/tokens/tokens.css +87 -87
  228. package/dist/tokens/typography-base.css +163 -163
  229. package/dist/tokens/utilities.css +353 -353
  230. package/dist/utils/apiConfig.spec.js +219 -219
  231. package/dist/utils/transitions.js +4 -4
  232. package/dist/utils/utils.js +693 -693
  233. package/package.json +297 -297
@@ -1,53 +1,53 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import type { HTMLAttributes } from 'svelte/elements';
4
-
5
- interface Props extends HTMLAttributes<HTMLElement> {
6
- /** The semantic element to render */
7
- variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p';
8
- /** Font weight variant */
9
- weight?: 'bold' | 'semibold' | 'medium' | 'regular';
10
- /** Additional CSS classes */
11
- class?: string;
12
- /** Text content (alternative to children) */
13
- text?: string;
14
- /** Child content */
15
- children?: Snippet;
16
- }
17
-
18
- let {
19
- variant = 'p',
20
- weight = 'regular',
21
- class: className = '',
22
- text = '',
23
- children,
24
- ...restProps
25
- }: Props = $props();
26
-
27
- const sizeMap: Record<string, string> = {
28
- h1: 'text-3xl',
29
- h2: 'text-2xl',
30
- h3: 'text-xl',
31
- h4: 'text-lg',
32
- h5: 'text-base',
33
- h6: 'text-xs',
34
- p: 'text-sm',
35
- };
36
-
37
- const weightMap: Record<string, string> = {
38
- bold: 'font-bold',
39
- semibold: 'font-semibold',
40
- medium: 'font-medium',
41
- regular: 'font-normal',
42
- };
43
-
44
- let computedClass = $derived(`${sizeMap[variant]} ${weightMap[weight]} ${className}`.trim());
45
- </script>
46
-
47
- <svelte:element this={variant} class={computedClass} {...restProps}>
48
- {#if text}
49
- {text}
50
- {:else}
51
- {@render children?.()}
52
- {/if}
53
- </svelte:element>
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+
5
+ interface Props extends HTMLAttributes<HTMLElement> {
6
+ /** The semantic element to render */
7
+ variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p';
8
+ /** Font weight variant */
9
+ weight?: 'bold' | 'semibold' | 'medium' | 'regular';
10
+ /** Additional CSS classes */
11
+ class?: string;
12
+ /** Text content (alternative to children) */
13
+ text?: string;
14
+ /** Child content */
15
+ children?: Snippet;
16
+ }
17
+
18
+ let {
19
+ variant = 'p',
20
+ weight = 'regular',
21
+ class: className = '',
22
+ text = '',
23
+ children,
24
+ ...restProps
25
+ }: Props = $props();
26
+
27
+ const sizeMap: Record<string, string> = {
28
+ h1: 'text-3xl',
29
+ h2: 'text-2xl',
30
+ h3: 'text-xl',
31
+ h4: 'text-lg',
32
+ h5: 'text-base',
33
+ h6: 'text-xs',
34
+ p: 'text-sm',
35
+ };
36
+
37
+ const weightMap: Record<string, string> = {
38
+ bold: 'font-bold',
39
+ semibold: 'font-semibold',
40
+ medium: 'font-medium',
41
+ regular: 'font-normal',
42
+ };
43
+
44
+ let computedClass = $derived(`${sizeMap[variant]} ${weightMap[weight]} ${className}`.trim());
45
+ </script>
46
+
47
+ <svelte:element this={variant} class={computedClass} {...restProps}>
48
+ {#if text}
49
+ {text}
50
+ {:else}
51
+ {@render children?.()}
52
+ {/if}
53
+ </svelte:element>
@@ -1,103 +1,103 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render } from '@testing-library/svelte';
3
- import ValidationError from './ValidationError.svelte';
4
-
5
- describe('ValidationError Component', () => {
6
- it('renders error message when error prop is provided', () => {
7
- const { container, getByText } = render(ValidationError, {
8
- props: { error: 'This field is required' },
9
- });
10
- expect(getByText('This field is required')).toBeTruthy();
11
- });
12
-
13
- it('does not render when error is empty', () => {
14
- const { container } = render(ValidationError, {
15
- props: { error: '' },
16
- });
17
- const alert = container.querySelector('[role="alert"]');
18
- expect(alert).toBeFalsy();
19
- });
20
-
21
- it('does not render when show is false', () => {
22
- const { container } = render(ValidationError, {
23
- props: { error: 'Error message', show: false },
24
- });
25
- const alert = container.querySelector('[role="alert"]');
26
- expect(alert).toBeFalsy();
27
- });
28
-
29
- it('has role="alert" attribute', () => {
30
- const { container } = render(ValidationError, {
31
- props: { error: 'Error message' },
32
- });
33
- const alert = container.querySelector('[role="alert"]');
34
- expect(alert).toBeTruthy();
35
- });
36
-
37
- it('has red text styling', () => {
38
- const { container } = render(ValidationError, {
39
- props: { error: 'Error message' },
40
- });
41
- const alert = container.querySelector('[role="alert"]');
42
- expect(alert.classList.contains('text-red-600')).toBe(true);
43
- });
44
-
45
- it('renders WarningIcon', () => {
46
- const { container } = render(ValidationError, {
47
- props: { error: 'Error message' },
48
- });
49
- // The WarningIcon should render an SVG
50
- const svg = container.querySelector('svg');
51
- expect(svg).toBeTruthy();
52
- });
53
-
54
- it('renders error text in a paragraph', () => {
55
- const { container } = render(ValidationError, {
56
- props: { error: 'Custom error text' },
57
- });
58
- const paragraph = container.querySelector('p');
59
- expect(paragraph).toBeTruthy();
60
- expect(paragraph.textContent).toBe('Custom error text');
61
- });
62
-
63
- it('shows when show is true and error exists', () => {
64
- const { container } = render(ValidationError, {
65
- props: { error: 'Error message', show: true },
66
- });
67
- const alert = container.querySelector('[role="alert"]');
68
- expect(alert).toBeTruthy();
69
- });
70
-
71
- it('renders with flex layout', () => {
72
- const { container } = render(ValidationError, {
73
- props: { error: 'Error message' },
74
- });
75
- const alert = container.querySelector('[role="alert"]');
76
- expect(alert.classList.contains('flex')).toBe(true);
77
- expect(alert.classList.contains('items-start')).toBe(true);
78
- });
79
-
80
- it('has aria-live="assertive" for screen reader announcements', () => {
81
- const { container } = render(ValidationError, {
82
- props: { error: 'Error message' },
83
- });
84
- const alert = container.querySelector('[role="alert"]');
85
- expect(alert.getAttribute('aria-live')).toBe('assertive');
86
- });
87
-
88
- it('renders with id when provided', () => {
89
- const { container } = render(ValidationError, {
90
- props: { error: 'Error message', id: 'my-error-id' },
91
- });
92
- const alert = container.querySelector('[role="alert"]');
93
- expect(alert.getAttribute('id')).toBe('my-error-id');
94
- });
95
-
96
- it('does not render id when not provided', () => {
97
- const { container } = render(ValidationError, {
98
- props: { error: 'Error message' },
99
- });
100
- const alert = container.querySelector('[role="alert"]');
101
- expect(alert.getAttribute('id')).toBeFalsy();
102
- });
103
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { render } from '@testing-library/svelte';
3
+ import ValidationError from './ValidationError.svelte';
4
+
5
+ describe('ValidationError Component', () => {
6
+ it('renders error message when error prop is provided', () => {
7
+ const { container, getByText } = render(ValidationError, {
8
+ props: { error: 'This field is required' },
9
+ });
10
+ expect(getByText('This field is required')).toBeTruthy();
11
+ });
12
+
13
+ it('does not render when error is empty', () => {
14
+ const { container } = render(ValidationError, {
15
+ props: { error: '' },
16
+ });
17
+ const alert = container.querySelector('[role="alert"]');
18
+ expect(alert).toBeFalsy();
19
+ });
20
+
21
+ it('does not render when show is false', () => {
22
+ const { container } = render(ValidationError, {
23
+ props: { error: 'Error message', show: false },
24
+ });
25
+ const alert = container.querySelector('[role="alert"]');
26
+ expect(alert).toBeFalsy();
27
+ });
28
+
29
+ it('has role="alert" attribute', () => {
30
+ const { container } = render(ValidationError, {
31
+ props: { error: 'Error message' },
32
+ });
33
+ const alert = container.querySelector('[role="alert"]');
34
+ expect(alert).toBeTruthy();
35
+ });
36
+
37
+ it('has red text styling', () => {
38
+ const { container } = render(ValidationError, {
39
+ props: { error: 'Error message' },
40
+ });
41
+ const alert = container.querySelector('[role="alert"]');
42
+ expect(alert.classList.contains('text-red-600')).toBe(true);
43
+ });
44
+
45
+ it('renders WarningIcon', () => {
46
+ const { container } = render(ValidationError, {
47
+ props: { error: 'Error message' },
48
+ });
49
+ // The WarningIcon should render an SVG
50
+ const svg = container.querySelector('svg');
51
+ expect(svg).toBeTruthy();
52
+ });
53
+
54
+ it('renders error text in a paragraph', () => {
55
+ const { container } = render(ValidationError, {
56
+ props: { error: 'Custom error text' },
57
+ });
58
+ const paragraph = container.querySelector('p');
59
+ expect(paragraph).toBeTruthy();
60
+ expect(paragraph.textContent).toBe('Custom error text');
61
+ });
62
+
63
+ it('shows when show is true and error exists', () => {
64
+ const { container } = render(ValidationError, {
65
+ props: { error: 'Error message', show: true },
66
+ });
67
+ const alert = container.querySelector('[role="alert"]');
68
+ expect(alert).toBeTruthy();
69
+ });
70
+
71
+ it('renders with flex layout', () => {
72
+ const { container } = render(ValidationError, {
73
+ props: { error: 'Error message' },
74
+ });
75
+ const alert = container.querySelector('[role="alert"]');
76
+ expect(alert.classList.contains('flex')).toBe(true);
77
+ expect(alert.classList.contains('items-start')).toBe(true);
78
+ });
79
+
80
+ it('has aria-live="assertive" for screen reader announcements', () => {
81
+ const { container } = render(ValidationError, {
82
+ props: { error: 'Error message' },
83
+ });
84
+ const alert = container.querySelector('[role="alert"]');
85
+ expect(alert.getAttribute('aria-live')).toBe('assertive');
86
+ });
87
+
88
+ it('renders with id when provided', () => {
89
+ const { container } = render(ValidationError, {
90
+ props: { error: 'Error message', id: 'my-error-id' },
91
+ });
92
+ const alert = container.querySelector('[role="alert"]');
93
+ expect(alert.getAttribute('id')).toBe('my-error-id');
94
+ });
95
+
96
+ it('does not render id when not provided', () => {
97
+ const { container } = render(ValidationError, {
98
+ props: { error: 'Error message' },
99
+ });
100
+ const alert = container.querySelector('[role="alert"]');
101
+ expect(alert.getAttribute('id')).toBeFalsy();
102
+ });
103
+ });
@@ -1,49 +1,49 @@
1
- <script module>
2
- import { defineMeta } from "@storybook/addon-svelte-csf";
3
-
4
- import ValidationError from './ValidationError.svelte';
5
-
6
- const { Story } = defineMeta({
7
- title: 'Components/ValidationError',
8
- component: ValidationError,
9
- tags: ['autodocs'],
10
- argTypes: {
11
- error: { control: 'text' },
12
- show: { control: 'boolean' },
13
- id: { control: 'text' },
14
- },
15
- });
16
-
17
- </script>
18
-
19
- <script>
20
- </script>
21
-
22
-
23
- <Story
24
- name="Default"
25
- args={{
26
- error: 'This field is required',
27
- show: true,
28
- }}
29
- />
30
-
31
- <Story
32
- name="Email Validation"
33
- args={{
34
- error: 'Please enter a valid email address',
35
- show: true,
36
- }}
37
- />
38
-
39
- <Story
40
- name="Password Validation"
41
- args={{
42
- error: 'Password must be at least 8 characters',
43
- show: true,
44
- }}
45
- />
46
-
1
+ <script module>
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+
4
+ import ValidationError from './ValidationError.svelte';
5
+
6
+ const { Story } = defineMeta({
7
+ title: 'Components/ValidationError',
8
+ component: ValidationError,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ error: { control: 'text' },
12
+ show: { control: 'boolean' },
13
+ id: { control: 'text' },
14
+ },
15
+ });
16
+
17
+ </script>
18
+
19
+ <script>
20
+ </script>
21
+
22
+
23
+ <Story
24
+ name="Default"
25
+ args={{
26
+ error: 'This field is required',
27
+ show: true,
28
+ }}
29
+ />
30
+
31
+ <Story
32
+ name="Email Validation"
33
+ args={{
34
+ error: 'Please enter a valid email address',
35
+ show: true,
36
+ }}
37
+ />
38
+
39
+ <Story
40
+ name="Password Validation"
41
+ args={{
42
+ error: 'Password must be at least 8 characters',
43
+ show: true,
44
+ }}
45
+ />
46
+
47
47
  <Story name="With Input Field">
48
48
  <div class="max-w-md">
49
49
  <label for="email-field" class="block text-sm font-medium mb-1">Email Address</label>
@@ -87,26 +87,26 @@
87
87
  <p class="text-sm text-gray-500 dark:text-gray-400 mt-2">Validation error is hidden until field is touched</p>
88
88
  </div>
89
89
  </Story>
90
-
91
- <Story name="Accessible with aria-describedby">
92
- <div class="max-w-md">
93
- <label for="accessible-email" class="block text-sm font-medium mb-1">Email Address</label>
94
- <input
95
- id="accessible-email"
96
- type="email"
97
- aria-describedby="email-error"
98
- aria-invalid="true"
99
- class="w-full px-3 py-2 border border-red-300 rounded focus:ring-red-500 focus:border-red-500"
100
- value="invalid-email"
101
- />
102
- <ValidationError
103
- id="email-error"
104
- error="Please enter a valid email address"
105
- show={true}
106
- />
107
- <p class="text-sm text-gray-500 mt-4">
108
- This example shows proper accessibility: the input references the error via aria-describedby,
109
- and the error has aria-live="assertive" for screen reader announcements.
110
- </p>
111
- </div>
112
- </Story>
90
+
91
+ <Story name="Accessible with aria-describedby">
92
+ <div class="max-w-md">
93
+ <label for="accessible-email" class="block text-sm font-medium mb-1">Email Address</label>
94
+ <input
95
+ id="accessible-email"
96
+ type="email"
97
+ aria-describedby="email-error"
98
+ aria-invalid="true"
99
+ class="w-full px-3 py-2 border border-red-300 rounded focus:ring-red-500 focus:border-red-500"
100
+ value="invalid-email"
101
+ />
102
+ <ValidationError
103
+ id="email-error"
104
+ error="Please enter a valid email address"
105
+ show={true}
106
+ />
107
+ <p class="text-sm text-gray-500 mt-4">
108
+ This example shows proper accessibility: the input references the error via aria-describedby,
109
+ and the error has aria-live="assertive" for screen reader announcements.
110
+ </p>
111
+ </div>
112
+ </Story>
@@ -1,29 +1,29 @@
1
- <script lang="ts">
2
- import { safeSlide } from "../utils/transitions.js";
3
- import { cubicOut } from "svelte/easing";
4
- import WarningIcon from "./Icons/WarningIcon.svelte";
5
-
6
- interface Props {
7
- /** Error message to display */
8
- error?: string;
9
- /** Whether to show the error */
10
- show?: boolean;
11
- /** Optional ID for aria-describedby linking from input fields */
12
- id?: string;
13
- }
14
-
15
- let { error = '', show = true, id }: Props = $props();
16
- </script>
17
-
18
- {#if error && show}
19
- <div
20
- transition:safeSlide={{ duration: 300, easing: cubicOut }}
21
- class="flex items-start gap-1.5 text-red-600 dark:text-red-500 mt-2"
22
- role="alert"
23
- aria-live="assertive"
24
- {id}
25
- >
26
- <WarningIcon class="shrink-0 mt-[1px]" />
27
- <p class="text-sm leading-tight m-0">{error}</p>
28
- </div>
29
- {/if}
1
+ <script lang="ts">
2
+ import { safeSlide } from "../utils/transitions.js";
3
+ import { cubicOut } from "svelte/easing";
4
+ import WarningIcon from "./Icons/WarningIcon.svelte";
5
+
6
+ interface Props {
7
+ /** Error message to display */
8
+ error?: string;
9
+ /** Whether to show the error */
10
+ show?: boolean;
11
+ /** Optional ID for aria-describedby linking from input fields */
12
+ id?: string;
13
+ }
14
+
15
+ let { error = '', show = true, id }: Props = $props();
16
+ </script>
17
+
18
+ {#if error && show}
19
+ <div
20
+ transition:safeSlide={{ duration: 300, easing: cubicOut }}
21
+ class="flex items-start gap-1.5 text-red-600 dark:text-red-500 mt-2"
22
+ role="alert"
23
+ aria-live="assertive"
24
+ {id}
25
+ >
26
+ <WarningIcon class="shrink-0 mt-[1px]" />
27
+ <p class="text-sm leading-tight m-0">{error}</p>
28
+ </div>
29
+ {/if}