sonance-brand-mcp 1.2.5 → 1.3.2

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 (189) hide show
  1. package/dist/assets/api/sonance-analyze/route.ts +1116 -0
  2. package/dist/assets/api/sonance-assets/route.ts +113 -0
  3. package/dist/assets/api/sonance-components/route.ts +41 -0
  4. package/dist/assets/api/sonance-inject-id/route.ts +363 -0
  5. package/dist/assets/api/sonance-save-logo/route.ts +426 -0
  6. package/dist/assets/api/sonance-theme/route.ts +106 -0
  7. package/dist/assets/brand-system.ts +1265 -0
  8. package/dist/assets/components/accordion.stories.tsx +26 -26
  9. package/dist/assets/components/accordion.tsx +3 -3
  10. package/dist/assets/components/alert-dialog.stories.tsx +142 -0
  11. package/dist/assets/components/alert-dialog.tsx +143 -0
  12. package/dist/assets/components/alert.stories.tsx +3 -3
  13. package/dist/assets/components/alert.tsx +4 -3
  14. package/dist/assets/components/aspect-ratio.stories.tsx +70 -0
  15. package/dist/assets/components/aspect-ratio.tsx +8 -0
  16. package/dist/assets/components/autocomplete.stories.tsx +9 -9
  17. package/dist/assets/components/autocomplete.tsx +3 -3
  18. package/dist/assets/components/avatar.stories.tsx +5 -5
  19. package/dist/assets/components/avatar.tsx +67 -23
  20. package/dist/assets/components/badge.stories.tsx +10 -10
  21. package/dist/assets/components/badge.tsx +3 -3
  22. package/dist/assets/components/breadcrumbs.stories.tsx +7 -7
  23. package/dist/assets/components/breadcrumbs.tsx +13 -8
  24. package/dist/assets/components/button.stories.tsx +74 -74
  25. package/dist/assets/components/button.tsx +2 -0
  26. package/dist/assets/components/calendar.stories.tsx +11 -11
  27. package/dist/assets/components/calendar.tsx +4 -4
  28. package/dist/assets/components/card.stories.tsx +22 -22
  29. package/dist/assets/components/card.tsx +7 -3
  30. package/dist/assets/components/carousel.stories.tsx +158 -0
  31. package/dist/assets/components/carousel.tsx +264 -0
  32. package/dist/assets/components/chart.stories.tsx +376 -0
  33. package/dist/assets/components/chart.tsx +384 -0
  34. package/dist/assets/components/checkbox-group.stories.tsx +6 -6
  35. package/dist/assets/components/checkbox-group.tsx +3 -3
  36. package/dist/assets/components/checkbox.stories.tsx +23 -20
  37. package/dist/assets/components/checkbox.tsx +13 -6
  38. package/dist/assets/components/code.stories.tsx +24 -24
  39. package/dist/assets/components/code.tsx +22 -27
  40. package/dist/assets/components/collapsible.stories.tsx +128 -0
  41. package/dist/assets/components/collapsible.tsx +10 -0
  42. package/dist/assets/components/command.stories.tsx +183 -0
  43. package/dist/assets/components/command.tsx +171 -0
  44. package/dist/assets/components/context-menu.stories.tsx +159 -0
  45. package/dist/assets/components/context-menu.tsx +214 -0
  46. package/dist/assets/components/date-input.stories.tsx +9 -9
  47. package/dist/assets/components/date-input.tsx +2 -2
  48. package/dist/assets/components/date-picker.stories.tsx +9 -9
  49. package/dist/assets/components/date-picker.tsx +3 -3
  50. package/dist/assets/components/date-range-picker.stories.tsx +12 -12
  51. package/dist/assets/components/date-range-picker.tsx +3 -3
  52. package/dist/assets/components/dialog.stories.tsx +40 -40
  53. package/dist/assets/components/dialog.tsx +8 -12
  54. package/dist/assets/components/divider.stories.tsx +30 -30
  55. package/dist/assets/components/divider.tsx +34 -35
  56. package/dist/assets/components/drawer.stories.tsx +32 -31
  57. package/dist/assets/components/drawer.tsx +7 -6
  58. package/dist/assets/components/dropdown-menu.tsx +213 -0
  59. package/dist/assets/components/dropdown.stories.tsx +12 -12
  60. package/dist/assets/components/dropdown.tsx +5 -5
  61. package/dist/assets/components/form.stories.tsx +30 -29
  62. package/dist/assets/components/form.tsx +5 -5
  63. package/dist/assets/components/hover-card.stories.tsx +115 -0
  64. package/dist/assets/components/hover-card.tsx +35 -0
  65. package/dist/assets/components/image.stories.tsx +48 -25
  66. package/dist/assets/components/image.tsx +8 -5
  67. package/dist/assets/components/input-otp.stories.tsx +15 -15
  68. package/dist/assets/components/input-otp.tsx +5 -5
  69. package/dist/assets/components/input.stories.tsx +30 -25
  70. package/dist/assets/components/input.tsx +7 -4
  71. package/dist/assets/components/kbd.stories.tsx +34 -34
  72. package/dist/assets/components/kbd.tsx +9 -9
  73. package/dist/assets/components/link.stories.tsx +36 -36
  74. package/dist/assets/components/link.tsx +4 -0
  75. package/dist/assets/components/listbox.stories.tsx +5 -5
  76. package/dist/assets/components/listbox.tsx +4 -4
  77. package/dist/assets/components/menubar.stories.tsx +208 -0
  78. package/dist/assets/components/menubar.tsx +247 -0
  79. package/dist/assets/components/navbar.stories.tsx +24 -24
  80. package/dist/assets/components/navbar.tsx +8 -14
  81. package/dist/assets/components/navigation-menu.stories.tsx +239 -0
  82. package/dist/assets/components/navigation-menu.tsx +135 -0
  83. package/dist/assets/components/number-input.stories.tsx +11 -11
  84. package/dist/assets/components/number-input.tsx +3 -3
  85. package/dist/assets/components/pagination.stories.tsx +13 -13
  86. package/dist/assets/components/pagination.tsx +6 -6
  87. package/dist/assets/components/popover.stories.tsx +35 -35
  88. package/dist/assets/components/popover.tsx +98 -15
  89. package/dist/assets/components/progress.stories.tsx +5 -5
  90. package/dist/assets/components/progress.tsx +5 -5
  91. package/dist/assets/components/radio-group.stories.tsx +7 -7
  92. package/dist/assets/components/radio-group.tsx +3 -3
  93. package/dist/assets/components/range-calendar.stories.tsx +18 -18
  94. package/dist/assets/components/range-calendar.tsx +3 -3
  95. package/dist/assets/components/resizable.stories.tsx +197 -0
  96. package/dist/assets/components/resizable.tsx +47 -0
  97. package/dist/assets/components/scroll-area.stories.tsx +123 -0
  98. package/dist/assets/components/scroll-area.tsx +48 -0
  99. package/dist/assets/components/scroll-shadow.stories.tsx +17 -17
  100. package/dist/assets/components/scroll-shadow.tsx +31 -9
  101. package/dist/assets/components/select.stories.tsx +20 -19
  102. package/dist/assets/components/select.tsx +10 -6
  103. package/dist/assets/components/separator.tsx +32 -0
  104. package/dist/assets/components/sheet.tsx +137 -0
  105. package/dist/assets/components/sidebar.stories.tsx +351 -0
  106. package/dist/assets/components/sidebar.tsx +757 -0
  107. package/dist/assets/components/skeleton.stories.tsx +3 -3
  108. package/dist/assets/components/skeleton.tsx +2 -2
  109. package/dist/assets/components/slider.stories.tsx +6 -6
  110. package/dist/assets/components/slider.tsx +3 -3
  111. package/dist/assets/components/spacer.stories.tsx +11 -11
  112. package/dist/assets/components/spacer.tsx +2 -2
  113. package/dist/assets/components/spinner.stories.tsx +8 -8
  114. package/dist/assets/components/spinner.tsx +5 -5
  115. package/dist/assets/components/switch.stories.tsx +24 -20
  116. package/dist/assets/components/switch.tsx +14 -6
  117. package/dist/assets/components/table.stories.tsx +7 -7
  118. package/dist/assets/components/table.tsx +8 -8
  119. package/dist/assets/components/tabs.stories.tsx +37 -37
  120. package/dist/assets/components/tabs.tsx +3 -3
  121. package/dist/assets/components/textarea.stories.tsx +13 -12
  122. package/dist/assets/components/textarea.tsx +3 -3
  123. package/dist/assets/components/theme-toggle.stories.tsx +31 -30
  124. package/dist/assets/components/theme-toggle.tsx +2 -2
  125. package/dist/assets/components/time-input.stories.tsx +16 -16
  126. package/dist/assets/components/time-input.tsx +2 -2
  127. package/dist/assets/components/toast.stories.tsx +8 -5
  128. package/dist/assets/components/toast.tsx +6 -6
  129. package/dist/assets/components/toggle-group.stories.tsx +153 -0
  130. package/dist/assets/components/toggle-group.tsx +61 -0
  131. package/dist/assets/components/toggle.stories.tsx +77 -0
  132. package/dist/assets/components/toggle.tsx +46 -0
  133. package/dist/assets/components/tooltip.stories.tsx +49 -27
  134. package/dist/assets/components/tooltip.tsx +23 -90
  135. package/dist/assets/components/user.stories.tsx +23 -23
  136. package/dist/assets/components/user.tsx +7 -4
  137. package/dist/assets/dev-tools/SonanceDevTools.tsx +4201 -0
  138. package/dist/assets/dev-tools/index.ts +10 -0
  139. package/dist/assets/globals.css +39 -0
  140. package/dist/assets/logos/40th-anniversary/Sonance_40_Logo_CMYK_BEAM_BLUE_40_AND_BEAM_DARK.png +0 -0
  141. package/dist/assets/logos/Sonance logo dark mode.png +0 -0
  142. package/dist/assets/logos/Sonance logo light mode.png +0 -0
  143. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_2C_Light_RGB_05162025.png +0 -0
  144. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_3C_Dark_RGB_05162025.png +0 -0
  145. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_White_RGB_05162025.png +0 -0
  146. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Dark_RGB.png +0 -0
  147. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Light_RGB.png +0 -0
  148. package/dist/assets/logos/james/James_Logo_Black_CMYK.png +0 -0
  149. package/dist/assets/logos/james/James_Logo_Black_RGB.png +0 -0
  150. package/dist/assets/logos/james/James_Logo_LtGray_CMYK.png +0 -0
  151. package/dist/assets/logos/james/James_Logo_LtGray_RGB.png +0 -0
  152. package/dist/assets/logos/james/James_Logo_Polished_RGB.png +0 -0
  153. package/dist/assets/logos/james/James_Logo_Reverse_CMYK.png +0 -0
  154. package/dist/assets/logos/james/James_Logo_Reverse_RGB.png +0 -0
  155. package/dist/assets/logos/james/James_Logo_White_CMYK.png +0 -0
  156. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Dark_RGB.png +0 -0
  157. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Light_RGB.png +0 -0
  158. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Dark_RGB.png +0 -0
  159. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Light_RGB.png +0 -0
  160. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Reverse_RGB.png +0 -0
  161. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Black_RGB.png +0 -0
  162. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Reverse_RGB.png +0 -0
  163. package/dist/assets/logos/sonance/Sonance_Logo_2C_Dark_RGB.png +0 -0
  164. package/dist/assets/logos/sonance/Sonance_Logo_2C_Light_RGB.png +0 -0
  165. package/dist/assets/logos/sonance/Sonance_Logo_2C_Reverse_RGB.png +0 -0
  166. package/dist/assets/logos/sonance/Sonance_Logo_Black_RGB.png +0 -0
  167. package/dist/assets/logos/sonance/Sonance_Logo_Grayscale_RGB.png +0 -0
  168. package/dist/assets/logos/sonance/Sonance_Logo_Reverse_RGB.png +0 -0
  169. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Dark_CMYK.png +0 -0
  170. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Light_CMYK.png +0 -0
  171. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Dark_RGB.png +0 -0
  172. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Light_RGB.png +0 -0
  173. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Reverse_RGB.png +0 -0
  174. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Black_RGB.png +0 -0
  175. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Grayscale_RGB.png +0 -0
  176. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Reverse_RGB.png +0 -0
  177. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Dark.png +0 -0
  178. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Light.png +0 -0
  179. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Dark.png +0 -0
  180. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Light.png +0 -0
  181. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Dark.png +0 -0
  182. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Light.png +0 -0
  183. package/dist/assets/logos/trufig/TrufigLogo_Black.png +0 -0
  184. package/dist/assets/logos/trufig/TrufigLogo_Light.png +0 -0
  185. package/dist/assets/logos/trufig/TrufigWatermark_Black.png +0 -0
  186. package/dist/assets/logos/trufig/TrufigWatermark_Light.png +0 -0
  187. package/dist/assets/styles/brand-overrides.css +37 -0
  188. package/dist/index.js +2055 -15
  189. package/package.json +1 -1
@@ -41,8 +41,8 @@ type Story = StoryObj<typeof meta>;
41
41
 
42
42
  const sampleContent = Array.from({ length: 20 }, (_, i) => (
43
43
  <div key={i} className="p-4 border-b border-border">
44
- <h4 className="font-medium">Item {i + 1}</h4>
45
- <p className="text-sm text-foreground-muted">
44
+ <h4 id="h4-item-i-1" className="font-medium">Item {i + 1}</h4>
45
+ <p id="p-this-is-sample-conte" className="text-sm text-foreground-muted">
46
46
  This is sample content for item {i + 1}. Scroll to see the shadow effect.
47
47
  </p>
48
48
  </div>
@@ -53,8 +53,8 @@ const horizontalItems = Array.from({ length: 15 }, (_, i) => (
53
53
  key={i}
54
54
  className="flex-shrink-0 w-48 h-32 p-4 border border-border rounded-sm bg-card"
55
55
  >
56
- <h4 className="font-medium">Card {i + 1}</h4>
57
- <p className="text-sm text-foreground-muted">Scroll horizontally</p>
56
+ <h4 id="h4-card-i-1" className="font-medium">Card {i + 1}</h4>
57
+ <p id="p-scroll-horizontally" className="text-sm text-foreground-muted">Scroll horizontally</p>
58
58
  </div>
59
59
  ));
60
60
 
@@ -141,7 +141,7 @@ export const LargeShadow: Story = {
141
141
  export const HiddenScrollbar: Story = {
142
142
  render: () => (
143
143
  <div className="space-y-4">
144
- <p className="text-sm text-foreground-muted">
144
+ <p id="hidden-scrollbar-p-scrollbar-is-hidden-" className="text-sm text-foreground-muted">
145
145
  Scrollbar is hidden but scrolling still works
146
146
  </p>
147
147
  <ScrollShadow
@@ -171,19 +171,19 @@ export const ProductListExample: Story = {
171
171
  ];
172
172
 
173
173
  return (
174
- <div className="w-80 border border-border rounded-sm">
174
+ <div data-sonance-name="scroll-shadow.stories" className="w-80 border border-border rounded-sm">
175
175
  <div className="p-4 border-b border-border">
176
- <h3 className="font-medium">Products ({products.length})</h3>
176
+ <h3 id="product-list-example-h3-products-productslen" className="font-medium">Products ({products.length})</h3>
177
177
  </div>
178
178
  <ScrollShadow className="h-64">
179
179
  {products.map((product, i) => (
180
180
  <div key={i} className="p-4 border-b border-border hover:bg-secondary-hover transition-colors cursor-pointer">
181
181
  <div className="flex justify-between items-start">
182
182
  <div>
183
- <h4 className="font-medium text-sm">{product.name}</h4>
184
- <p className="text-xs text-foreground-muted">{product.category}</p>
183
+ <h4 id="product-list-example-h4-productname" className="font-medium text-sm">{product.name}</h4>
184
+ <p id="product-list-example-p-productcategory" className="text-xs text-foreground-muted">{product.category}</p>
185
185
  </div>
186
- <span className="font-medium">{product.price}</span>
186
+ <span id="product-list-example-span-productprice" className="font-medium">{product.price}</span>
187
187
  </div>
188
188
  </div>
189
189
  ))}
@@ -203,7 +203,7 @@ export const ImageGalleryExample: Story = {
203
203
 
204
204
  return (
205
205
  <div className="space-y-2">
206
- <h3 className="font-medium">Gallery</h3>
206
+ <h3 id="image-gallery-example-h3-gallery" className="font-medium">Gallery</h3>
207
207
  <ScrollShadow
208
208
  orientation="horizontal"
209
209
  hideScrollbar
@@ -265,19 +265,19 @@ export const AllSizesComparison: Story = {
265
265
  render: () => (
266
266
  <div className="flex gap-8">
267
267
  <div className="space-y-2">
268
- <p className="text-sm font-medium">Small</p>
268
+ <p id="all-sizes-comparison-p-small" className="text-sm font-medium">Small</p>
269
269
  <ScrollShadow size="sm" className="h-48 w-64 border border-border rounded-sm">
270
270
  {sampleContent.slice(0, 10)}
271
271
  </ScrollShadow>
272
272
  </div>
273
273
  <div className="space-y-2">
274
- <p className="text-sm font-medium">Medium</p>
274
+ <p id="all-sizes-comparison-p-medium" className="text-sm font-medium">Medium</p>
275
275
  <ScrollShadow size="md" className="h-48 w-64 border border-border rounded-sm">
276
276
  {sampleContent.slice(0, 10)}
277
277
  </ScrollShadow>
278
278
  </div>
279
279
  <div className="space-y-2">
280
- <p className="text-sm font-medium">Large</p>
280
+ <p id="all-sizes-comparison-p-large" className="text-sm font-medium">Large</p>
281
281
  <ScrollShadow size="lg" className="h-48 w-64 border border-border rounded-sm">
282
282
  {sampleContent.slice(0, 10)}
283
283
  </ScrollShadow>
@@ -292,7 +292,7 @@ export const ResponsiveMatrix: Story = {
292
292
  <div className="space-y-8">
293
293
  {/* Mobile */}
294
294
  <div>
295
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
295
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
296
296
  <div className="w-[375px] border border-dashed border-border p-4">
297
297
  <ScrollShadow className="h-48 border border-border rounded-sm">
298
298
  {sampleContent.slice(0, 8)}
@@ -301,7 +301,7 @@ export const ResponsiveMatrix: Story = {
301
301
  </div>
302
302
  {/* Tablet */}
303
303
  <div>
304
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
304
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
305
305
  <div className="w-[768px] border border-dashed border-border p-4">
306
306
  <ScrollShadow
307
307
  orientation="horizontal"
@@ -315,7 +315,7 @@ export const ResponsiveMatrix: Story = {
315
315
  </div>
316
316
  {/* Desktop */}
317
317
  <div>
318
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
318
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
319
319
  <div className="w-[1280px] border border-dashed border-border p-4">
320
320
  <div className="grid grid-cols-3 gap-4">
321
321
  <ScrollShadow size="sm" className="h-48 border border-border rounded-sm">
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useRef, useState, useEffect, ReactNode } from "react";
3
+ import { useRef, useState, useEffect, ReactNode, useMemo } from "react";
4
4
  import { cn } from "@/lib/utils";
5
5
 
6
6
  interface ScrollShadowProps {
@@ -12,6 +12,19 @@ interface ScrollShadowProps {
12
12
  style?: React.CSSProperties;
13
13
  }
14
14
 
15
+ // Responsive size maps for different screen sizes
16
+ const mobileSizeMap = {
17
+ sm: 12,
18
+ md: 24,
19
+ lg: 36,
20
+ };
21
+
22
+ const desktopSizeMap = {
23
+ sm: 20,
24
+ md: 40,
25
+ lg: 60,
26
+ };
27
+
15
28
  export function ScrollShadow({
16
29
  children,
17
30
  orientation = "vertical",
@@ -23,14 +36,23 @@ export function ScrollShadow({
23
36
  const scrollRef = useRef<HTMLDivElement>(null);
24
37
  const [showStartShadow, setShowStartShadow] = useState(false);
25
38
  const [showEndShadow, setShowEndShadow] = useState(false);
39
+ const [isMobile, setIsMobile] = useState(false);
26
40
 
27
- const sizeMap = {
28
- sm: 20,
29
- md: 40,
30
- lg: 60,
31
- };
41
+ // Detect mobile screen size
42
+ useEffect(() => {
43
+ const checkMobile = () => {
44
+ setIsMobile(window.innerWidth < 640); // sm breakpoint
45
+ };
46
+
47
+ checkMobile();
48
+ window.addEventListener("resize", checkMobile);
49
+ return () => window.removeEventListener("resize", checkMobile);
50
+ }, []);
32
51
 
33
- const shadowSize = sizeMap[size];
52
+ const shadowSize = useMemo(() => {
53
+ const sizeMap = isMobile ? mobileSizeMap : desktopSizeMap;
54
+ return sizeMap[size];
55
+ }, [isMobile, size]);
34
56
 
35
57
  const updateShadows = () => {
36
58
  const element = scrollRef.current;
@@ -70,9 +92,9 @@ export function ScrollShadow({
70
92
  const isHorizontal = orientation === "horizontal" || orientation === "both";
71
93
 
72
94
  return (
73
- <div className={cn("relative", className)} style={style}>
95
+ <div data-sonance-name="scroll-shadow" className={cn("relative", className)} style={style}>
74
96
  {/* Start shadow (top/left) */}
75
- <div
97
+ <div data-sonance-name="scroll-shadow"
76
98
  className={cn(
77
99
  "pointer-events-none absolute z-10 transition-opacity duration-200",
78
100
  isVertical && "inset-x-0 top-0",
@@ -101,11 +101,11 @@ export const WithDisabledOption: Story = {
101
101
  export const AllStates: Story = {
102
102
  render: () => (
103
103
  <div className="space-y-6 w-72">
104
- <Select options={defaultOptions} placeholder="Default select" />
105
- <Select options={countryOptions} label="With Label" placeholder="Select..." />
106
- <Select options={countryOptions} label="With Value" defaultValue="uk" />
107
- <Select options={countryOptions} label="With Error" error="Required field" />
108
- <Select options={countryOptions} label="Disabled" defaultValue="us" disabled />
104
+ <Select id="all-states-select-default-select" options={defaultOptions} placeholder="Default select" />
105
+ <Select id="all-states-select" options={countryOptions} label="With Label" placeholder="Select..." />
106
+ <Select id="all-states-select" options={countryOptions} label="With Value" defaultValue="uk" />
107
+ <Select id="all-states-select" options={countryOptions} label="With Error" error="Required field" />
108
+ <Select id="all-states-select" options={countryOptions} label="Disabled" defaultValue="us" disabled />
109
109
  </div>
110
110
  ),
111
111
  };
@@ -132,13 +132,14 @@ export const StateMatrix: Story = {
132
132
  render: () => {
133
133
  const states: SelectState[] = ['default', 'hover', 'focus', 'open', 'error', 'disabled'];
134
134
  return (
135
- <div className="space-y-6 w-80">
136
- <h3 className="text-sm font-medium text-foreground-muted">Select States</h3>
135
+ <div data-sonance-name="select.stories" className="space-y-6 w-80">
136
+ <h3 id="state-matrix-h3-select-states" className="text-sm font-medium text-foreground-muted">Select States</h3>
137
137
  <div className="grid grid-cols-1 gap-4">
138
138
  {states.map((state) => (
139
139
  <div key={state} className="flex items-center gap-4">
140
- <span className="text-xs font-medium text-foreground-muted uppercase w-20">{state}</span>
140
+ <span id="state-matrix-span-state" className="text-xs font-medium text-foreground-muted uppercase w-20">{state}</span>
141
141
  <Select
142
+ id="state-matrix-select-select-option"
142
143
  state={state}
143
144
  options={defaultOptions}
144
145
  placeholder="Select option"
@@ -159,31 +160,31 @@ export const ResponsiveMatrix: Story = {
159
160
  <div className="space-y-8">
160
161
  {/* Mobile */}
161
162
  <div>
162
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
163
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
163
164
  <div className="w-[375px] border border-dashed border-border p-4 space-y-4">
164
- <Select options={countryOptions} label="Country" placeholder="Select..." />
165
- <Select options={defaultOptions} label="Option" placeholder="Choose..." />
165
+ <Select id="responsive-matrix-select" options={countryOptions} label="Country" placeholder="Select..." />
166
+ <Select id="responsive-matrix-select-choose" options={defaultOptions} label="Option" placeholder="Choose..." />
166
167
  </div>
167
168
  </div>
168
169
  {/* Tablet */}
169
170
  <div>
170
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
171
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
171
172
  <div className="w-[768px] border border-dashed border-border p-4">
172
173
  <div className="grid grid-cols-2 gap-4">
173
- <Select options={countryOptions} label="Country" placeholder="Select country..." />
174
- <Select options={defaultOptions} label="Category" placeholder="Select category..." />
174
+ <Select id="responsive-matrix-select-select-country" options={countryOptions} label="Country" placeholder="Select country..." />
175
+ <Select id="responsive-matrix-select-select-category" options={defaultOptions} label="Category" placeholder="Select category..." />
175
176
  </div>
176
177
  </div>
177
178
  </div>
178
179
  {/* Desktop */}
179
180
  <div>
180
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
181
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
181
182
  <div className="w-[1280px] border border-dashed border-border p-4">
182
183
  <div className="grid grid-cols-4 gap-4">
183
- <Select options={countryOptions} label="Country" defaultValue="us" />
184
- <Select options={defaultOptions} label="Default" placeholder="Select..." />
185
- <Select options={countryOptions} label="With Error" error="Required" />
186
- <Select options={countryOptions} label="Disabled" defaultValue="uk" disabled />
184
+ <Select id="responsive-matrix-select" options={countryOptions} label="Country" defaultValue="us" />
185
+ <Select id="responsive-matrix-select" options={defaultOptions} label="Default" placeholder="Select..." />
186
+ <Select id="responsive-matrix-select" options={countryOptions} label="With Error" error="Required" />
187
+ <Select id="responsive-matrix-select" options={countryOptions} label="Disabled" defaultValue="uk" disabled />
187
188
  </div>
188
189
  </div>
189
190
  </div>
@@ -39,6 +39,8 @@ interface SelectProps {
39
39
  className?: string;
40
40
  /** Visual state for Storybook/Figma documentation */
41
41
  state?: SelectState;
42
+ /** Style for the select button */
43
+ style?: React.CSSProperties;
42
44
  }
43
45
 
44
46
  export function Select({
@@ -52,6 +54,7 @@ export function Select({
52
54
  disabled = false,
53
55
  className,
54
56
  state,
57
+ style,
55
58
  }: SelectProps) {
56
59
  const isDisabled = disabled || state === "disabled";
57
60
  const hasError = error || state === "error";
@@ -94,7 +97,7 @@ export function Select({
94
97
  }, []);
95
98
 
96
99
  return (
97
- <div className={cn("w-full", className)} ref={containerRef}>
100
+ <div data-sonance-name="select" className={cn("w-full", className)} ref={containerRef}>
98
101
  {label && (
99
102
  <label className="mb-2 block text-xs font-medium uppercase tracking-widest text-foreground-muted">
100
103
  {label}
@@ -105,6 +108,7 @@ export function Select({
105
108
  type="button"
106
109
  onClick={() => !isDisabled && setIsOpen(!isOpen)}
107
110
  disabled={isDisabled}
111
+ style={style}
108
112
  className={cn(
109
113
  "flex w-full items-center justify-between border border-input-border bg-input px-4 py-3",
110
114
  "text-left text-foreground transition-colors duration-200",
@@ -115,7 +119,7 @@ export function Select({
115
119
  getStateStyles(state)
116
120
  )}
117
121
  >
118
- <span className={cn(!selectedOption && "text-input-placeholder")}>
122
+ <span id="span-selectedoptionlabel-" className={cn(!selectedOption && "text-input-placeholder")}>
119
123
  {selectedOption?.label || placeholder}
120
124
  </span>
121
125
  <ChevronDown
@@ -155,7 +159,7 @@ export function Select({
155
159
  </div>
156
160
  )}
157
161
  </div>
158
- {error && <p className="mt-1 text-sm text-error">{error}</p>}
162
+ {error && <p id="p-error" className="mt-1 text-sm text-error">{error}</p>}
159
163
  </div>
160
164
  );
161
165
  }
@@ -191,7 +195,7 @@ export const NativeSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(
191
195
  const hasError = error || state === "error";
192
196
 
193
197
  return (
194
- <div className="w-full">
198
+ <div data-sonance-name="select" className="w-full">
195
199
  {label && (
196
200
  <label className="mb-2 block text-xs font-medium uppercase tracking-widest text-foreground-muted">
197
201
  {label}
@@ -209,7 +213,7 @@ export const NativeSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(
209
213
  hasError && "border-error",
210
214
  getNativeSelectStateStyles(state),
211
215
  className
212
- )}
216
+ )} data-sonance-name="select"
213
217
  {...props}
214
218
  >
215
219
  {options.map((option) => (
@@ -220,7 +224,7 @@ export const NativeSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(
220
224
  </select>
221
225
  <ChevronDown className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-foreground-muted" />
222
226
  </div>
223
- {error && <p className="mt-1 text-sm text-error">{error}</p>}
227
+ {error && <p id="native-select-p-error" className="mt-1 text-sm text-error">{error}</p>}
224
228
  </div>
225
229
  );
226
230
  }
@@ -0,0 +1,32 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SeparatorPrimitive from "@radix-ui/react-separator"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Separator = React.forwardRef<
9
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
11
+ >(
12
+ (
13
+ { className, orientation = "horizontal", decorative = true, ...props },
14
+ ref
15
+ ) => (
16
+ <SeparatorPrimitive.Root data-sonance-name="separator"
17
+ ref={ref}
18
+ decorative={decorative}
19
+ orientation={orientation}
20
+ className={cn(
21
+ "shrink-0 bg-border",
22
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ )
28
+ )
29
+ Separator.displayName = SeparatorPrimitive.Root.displayName
30
+
31
+ export { Separator }
32
+
@@ -0,0 +1,137 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SheetPrimitive from "@radix-ui/react-dialog"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import { X } from "lucide-react"
7
+
8
+ import { cn } from "@/lib/utils"
9
+
10
+ const Sheet = SheetPrimitive.Root
11
+
12
+ const SheetTrigger = SheetPrimitive.Trigger
13
+
14
+ const SheetClose = SheetPrimitive.Close
15
+
16
+ const SheetPortal = SheetPrimitive.Portal
17
+
18
+ const SheetOverlay = React.forwardRef<
19
+ React.ElementRef<typeof SheetPrimitive.Overlay>,
20
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
21
+ >(({ className, ...props }, ref) => (
22
+ <SheetPrimitive.Overlay
23
+ className={cn(
24
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
25
+ className
26
+ )}
27
+ {...props}
28
+ ref={ref}
29
+ />
30
+ ))
31
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32
+
33
+ const sheetVariants = cva(
34
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
35
+ {
36
+ variants: {
37
+ side: {
38
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
39
+ bottom:
40
+ "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
41
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
42
+ right:
43
+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ side: "right",
48
+ },
49
+ }
50
+ )
51
+
52
+ interface SheetContentProps
53
+ extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
54
+ VariantProps<typeof sheetVariants> {}
55
+
56
+ const SheetContent = React.forwardRef<
57
+ React.ElementRef<typeof SheetPrimitive.Content>,
58
+ SheetContentProps
59
+ >(({ side = "right", className, children, ...props }, ref) => (
60
+ <SheetPortal>
61
+ <SheetOverlay />
62
+ <SheetPrimitive.Content
63
+ ref={ref}
64
+ className={cn(sheetVariants({ side }), className)}
65
+ {...props}
66
+ >
67
+ {children}
68
+ <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
69
+ <X className="h-4 w-4" />
70
+ <span id="sheet-content-span-close" className="sr-only">Close</span>
71
+ </SheetPrimitive.Close>
72
+ </SheetPrimitive.Content>
73
+ </SheetPortal>
74
+ ))
75
+ SheetContent.displayName = SheetPrimitive.Content.displayName
76
+
77
+ const SheetHeader = ({
78
+ className,
79
+ ...props
80
+ }: React.HTMLAttributes<HTMLDivElement>) => (
81
+ <div data-sonance-name="sheet"
82
+ {...props}
83
+ />
84
+ )
85
+ SheetHeader.displayName = "SheetHeader"
86
+
87
+ const SheetFooter = ({
88
+ className,
89
+ ...props
90
+ }: React.HTMLAttributes<HTMLDivElement>) => (
91
+ <div
92
+ className={cn(
93
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
94
+ className
95
+ )} data-sonance-name="sheet"
96
+ {...props}
97
+ />
98
+ )
99
+ SheetFooter.displayName = "SheetFooter"
100
+
101
+ const SheetTitle = React.forwardRef<
102
+ React.ElementRef<typeof SheetPrimitive.Title>,
103
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
104
+ >(({ className, ...props }, ref) => (
105
+ <SheetPrimitive.Title
106
+ ref={ref}
107
+ className={cn("text-lg font-semibold text-foreground", className)}
108
+ {...props}
109
+ />
110
+ ))
111
+ SheetTitle.displayName = SheetPrimitive.Title.displayName
112
+
113
+ const SheetDescription = React.forwardRef<
114
+ React.ElementRef<typeof SheetPrimitive.Description>,
115
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
116
+ >(({ className, ...props }, ref) => (
117
+ <SheetPrimitive.Description
118
+ ref={ref}
119
+ className={cn("text-sm text-muted-foreground", className)}
120
+ {...props}
121
+ />
122
+ ))
123
+ SheetDescription.displayName = SheetPrimitive.Description.displayName
124
+
125
+ export {
126
+ Sheet,
127
+ SheetPortal,
128
+ SheetOverlay,
129
+ SheetTrigger,
130
+ SheetClose,
131
+ SheetContent,
132
+ SheetHeader,
133
+ SheetFooter,
134
+ SheetTitle,
135
+ SheetDescription,
136
+ }
137
+