@gentleduck/registry-ui 0.2.11 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.turbo/turbo-test.log +27 -9
  2. package/CHANGELOG.md +25 -0
  3. package/package.json +1 -1
  4. package/src/accordion/accordion.tsx +1 -1
  5. package/src/alert/alert.tsx +1 -1
  6. package/src/alert-dialog/alert-dialog.tsx +7 -7
  7. package/src/avatar/avatar.tsx +1 -1
  8. package/src/breadcrumb/breadcrumb.tsx +2 -2
  9. package/src/button/button.tsx +1 -1
  10. package/src/card/card.tsx +1 -1
  11. package/src/carousel/__test__/carousel.test.tsx +27 -0
  12. package/src/carousel/carousel.tsx +5 -5
  13. package/src/chart/chart.tsx +1 -1
  14. package/src/collapsible/collapsible.tsx +1 -1
  15. package/src/combobox/combobox.tsx +1 -0
  16. package/src/command/command.tsx +13 -8
  17. package/src/context-menu/context-menu.tsx +6 -6
  18. package/src/dialog/dialog-responsive.tsx +5 -5
  19. package/src/dialog/dialog.tsx +13 -11
  20. package/src/direction/direction.tsx +2 -1
  21. package/src/drawer/drawer.tsx +5 -5
  22. package/src/dropdown-menu/dropdown-menu.tsx +6 -6
  23. package/src/empty/empty.tsx +1 -1
  24. package/src/field/field.tsx +2 -2
  25. package/src/hover-card/hover-card.tsx +1 -1
  26. package/src/input-group/input-group.tsx +1 -1
  27. package/src/input-otp/input-otp.tsx +1 -1
  28. package/src/item/item.tsx +5 -5
  29. package/src/menubar/menubar.tsx +8 -8
  30. package/src/navigation-menu/navigation-menu.tsx +4 -4
  31. package/src/pagination/__test__/pagination.test.tsx +35 -0
  32. package/src/pagination/pagination.tsx +16 -16
  33. package/src/popover/popover.tsx +1 -1
  34. package/src/resizable/resizable.tsx +1 -1
  35. package/src/select/select.tsx +6 -6
  36. package/src/sheet/sheet.tsx +5 -5
  37. package/src/sidebar/__test__/sidebar.test.tsx +39 -0
  38. package/src/sidebar/sidebar.tsx +2 -2
  39. package/src/table/table.tsx +1 -1
  40. package/src/tabs/tabs.tsx +1 -1
  41. package/src/tooltip/tooltip.tsx +1 -1
@@ -1,23 +1,41 @@
1
1
  $ bun test
2
- bun test v1.3.5 (1e86cebd)
2
+ bun test v1.3.10 (30e609e0)
3
+
4
+ ::group::src/pagination/__test__/pagination.test.tsx:
5
+ (pass) registry-ui pagination > PaginationWrapper prefers provided button icons over the default chevrons [17.00ms]
6
+ (pass) registry-ui pagination > PaginationWrapper still renders default chevrons when icons are not provided [3.00ms]
7
+
8
+ ::endgroup::
9
+
10
+ ::group::src/sidebar/__test__/sidebar.test.tsx:
11
+ (pass) registry-ui sidebar > SidebarTrigger prefers a provided icon over the default panel icon [1.00ms]
12
+ (pass) registry-ui sidebar > SidebarTrigger still renders the default panel icon when no icon is provided [1.00ms]
13
+
14
+ ::endgroup::
3
15
 
4
16
  ::group::src/chart/__test__/chart.test.tsx:
5
- (pass) registry-ui chart > ChartContainer server render does not emit invalid size warnings [25.00ms]
17
+ (pass) registry-ui chart > ChartContainer server render does not emit invalid size warnings [9.00ms]
18
+
19
+ ::endgroup::
20
+
21
+ ::group::src/carousel/__test__/carousel.test.tsx:
22
+ (pass) registry-ui carousel > CarouselPrevious prefers a provided icon over the default arrow icon [2.00ms]
23
+ (pass) registry-ui carousel > CarouselNext still renders the default arrow icon when no icon is provided [1.00ms]
6
24
 
7
25
  ::endgroup::
8
26
 
9
27
  ::group::src/button/__test__/button.test.tsx:
10
- (pass) registry-ui button > buttonVariants returns the shared base styles and defaults [1.00ms]
28
+ (pass) registry-ui button > buttonVariants returns the shared base styles and defaults
11
29
  (pass) registry-ui button > buttonVariants applies explicit variant and size overrides
12
30
  (pass) registry-ui button > button exports keep stable display names
13
- (pass) registry-ui button > Button renders loading state as a busy disabled native button [3.00ms]
14
- (pass) registry-ui button > Button preserves explicit disabled state even when loading is false
15
- (pass) registry-ui button > Button collapses into icon-only mode and hides secondary content [1.00ms]
31
+ (pass) registry-ui button > Button renders loading state as a busy disabled native button [1.00ms]
32
+ (pass) registry-ui button > Button preserves explicit disabled state even when loading is false [1.00ms]
33
+ (pass) registry-ui button > Button collapses into icon-only mode and hides secondary content
16
34
  (pass) registry-ui button > AnimationIcon renders left and right placements around children
17
35
 
18
36
  ::endgroup::
19
37
 
20
- 8 pass
38
+ 14 pass
21
39
  0 fail
22
- 25 expect() calls
23
- Ran 8 tests across 2 files. [368.00ms]
40
+ 43 expect() calls
41
+ Ran 14 tests across 5 files. [403.00ms]
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @gentleduck/registry-ui
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3ee8b3a: feat: add AI documentation chat and command menu enhancements
8
+
9
+ @gentleduck/docs:
10
+
11
+ - Add useAIChat hook for streaming AI chat with rAF-batched updates
12
+ - Add AIChatPanel component with markdown rendering, shiki syntax highlighting, and dynamic props
13
+ - Add AI toggle mode to CommandMenu with auto-switch on empty search results
14
+ - Add react-markdown and shiki as optional peer dependencies
15
+
16
+ @gentleduck/registry-ui:
17
+
18
+ - Add hideClose prop to DialogContent to conditionally hide the close button
19
+ - Add children prop to CommandInput for rendering extra elements in the input wrapper
20
+ - Add contentClassName prop to CommandDialog for dynamic dialog sizing
21
+
22
+ ## 0.2.12
23
+
24
+ ### Patch Changes
25
+
26
+ - 0fd319f: Update carousel, pagination, and sidebar components with tests and fixes.
27
+
3
28
  ## 0.2.11
4
29
 
5
30
  ### Patch Changes
package/package.json CHANGED
@@ -56,5 +56,5 @@
56
56
  "test": "bun test"
57
57
  },
58
58
  "type": "module",
59
- "version": "0.2.11"
59
+ "version": "0.3.0"
60
60
  }
@@ -244,4 +244,4 @@ const AccordionContent = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDi
244
244
  )
245
245
  AccordionContent.displayName = 'AccordionContent'
246
246
 
247
- export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
247
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }
@@ -49,4 +49,4 @@ const AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<H
49
49
  )
50
50
  AlertDescription.displayName = 'AlertDescription'
51
51
 
52
- export { Alert, AlertTitle, AlertDescription }
52
+ export { Alert, AlertDescription, AlertTitle }
@@ -101,14 +101,14 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
101
101
 
102
102
  export {
103
103
  AlertDialog,
104
- AlertDialogPortal,
105
- AlertDialogOverlay,
106
- AlertDialogTrigger,
104
+ AlertDialogAction,
105
+ AlertDialogCancel,
107
106
  AlertDialogContent,
108
- AlertDialogHeader,
107
+ AlertDialogDescription,
109
108
  AlertDialogFooter,
109
+ AlertDialogHeader,
110
+ AlertDialogOverlay,
111
+ AlertDialogPortal,
110
112
  AlertDialogTitle,
111
- AlertDialogDescription,
112
- AlertDialogAction,
113
- AlertDialogCancel,
113
+ AlertDialogTrigger,
114
114
  }
@@ -75,4 +75,4 @@ const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
75
75
  )
76
76
  AvatarGroup.displayName = 'AvatarGroup'
77
77
 
78
- export { Avatar, AvatarImage, AvatarFallback, AvatarGroup }
78
+ export { Avatar, AvatarFallback, AvatarGroup, AvatarImage }
@@ -110,10 +110,10 @@ BreadcrumbEllipsis.displayName = 'BreadcrumbEllipsis'
110
110
 
111
111
  export {
112
112
  Breadcrumb,
113
- BreadcrumbList,
113
+ BreadcrumbEllipsis,
114
114
  BreadcrumbItem,
115
115
  BreadcrumbLink,
116
+ BreadcrumbList,
116
117
  BreadcrumbPage,
117
118
  BreadcrumbSeparator,
118
- BreadcrumbEllipsis,
119
119
  }
@@ -76,4 +76,4 @@ function AnimationIcon({ children, animationIcon }: AnimationIconProps): React.J
76
76
  }
77
77
  AnimationIcon.displayName = 'AnimationIcon'
78
78
 
79
- export { Button, AnimationIcon }
79
+ export { AnimationIcon, Button }
package/src/card/card.tsx CHANGED
@@ -78,4 +78,4 @@ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDiv
78
78
  )
79
79
  CardFooter.displayName = 'CardFooter'
80
80
 
81
- export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent }
81
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
@@ -0,0 +1,27 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import * as React from 'react'
3
+ import { renderToStaticMarkup } from 'react-dom/server'
4
+ import { Carousel, CarouselNext, CarouselPrevious } from '../carousel'
5
+
6
+ describe('registry-ui carousel', () => {
7
+ test('CarouselPrevious prefers a provided icon over the default arrow icon', () => {
8
+ const html = renderToStaticMarkup(
9
+ <Carousel>
10
+ <CarouselPrevious icon={<span data-icon="custom-prev">P</span>} />
11
+ </Carousel>,
12
+ )
13
+
14
+ expect(html).toContain('data-icon="custom-prev"')
15
+ expect(html).not.toContain('lucide-arrow-left')
16
+ })
17
+
18
+ test('CarouselNext still renders the default arrow icon when no icon is provided', () => {
19
+ const html = renderToStaticMarkup(
20
+ <Carousel>
21
+ <CarouselNext />
22
+ </Carousel>,
23
+ )
24
+
25
+ expect(html).toContain('lucide-arrow-right')
26
+ })
27
+ })
@@ -154,7 +154,7 @@ CarouselItem.displayName = 'CarouselItem'
154
154
  const CarouselPrevious = React.forwardRef<
155
155
  React.ComponentRef<typeof Button>,
156
156
  React.ComponentPropsWithoutRef<typeof Button> & { text?: string }
157
- >(({ className, variant = 'outline', size = 'icon', text = 'Previous slide', ...props }, ref) => {
157
+ >(({ className, icon, variant = 'outline', size = 'icon', text = 'Previous slide', ...props }, ref) => {
158
158
  const { orientation, scrollPrev, canScrollPrev } = useCarousel()
159
159
 
160
160
  return (
@@ -172,8 +172,8 @@ const CarouselPrevious = React.forwardRef<
172
172
  ref={ref}
173
173
  size={size}
174
174
  variant={variant}
175
+ icon={icon === undefined ? <ArrowLeft aria-hidden="true" className="h-4 w-4 rtl:rotate-180" /> : icon}
175
176
  {...props}>
176
- <ArrowLeft aria-hidden="true" className="h-4 w-4 rtl:rotate-180" />
177
177
  <span className="sr-only">{text}</span>
178
178
  </Button>
179
179
  )
@@ -183,7 +183,7 @@ CarouselPrevious.displayName = 'CarouselPrevious'
183
183
  const CarouselNext = React.forwardRef<
184
184
  React.ComponentRef<typeof Button>,
185
185
  React.ComponentPropsWithoutRef<typeof Button> & { text?: string }
186
- >(({ className, variant = 'outline', size = 'icon', text = 'Next slide', ...props }, ref) => {
186
+ >(({ className, icon, variant = 'outline', size = 'icon', text = 'Next slide', ...props }, ref) => {
187
187
  const { orientation, scrollNext, canScrollNext } = useCarousel()
188
188
 
189
189
  return (
@@ -201,12 +201,12 @@ const CarouselNext = React.forwardRef<
201
201
  ref={ref}
202
202
  size={size}
203
203
  variant={variant}
204
+ icon={icon === undefined ? <ArrowRight aria-hidden="true" className="h-4 w-4 rtl:rotate-180" /> : icon}
204
205
  {...props}>
205
- <ArrowRight aria-hidden="true" className="h-4 w-4 rtl:rotate-180" />
206
206
  <span className="sr-only">{text}</span>
207
207
  </Button>
208
208
  )
209
209
  })
210
210
  CarouselNext.displayName = 'CarouselNext'
211
211
 
212
- export { type CarouselApi, Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext }
212
+ export { Carousel, type CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious }
@@ -266,4 +266,4 @@ function ChartLegendContent({
266
266
  }
267
267
  ChartLegendContent.displayName = 'ChartLegendContent'
268
268
 
269
- export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle }
269
+ export { ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent }
@@ -149,4 +149,4 @@ const CollapsibleContent = React.forwardRef<
149
149
  })
150
150
  CollapsibleContent.displayName = 'CollapsibleContent'
151
151
 
152
- export { Collapsible, CollapsibleTrigger, CollapsibleContent }
152
+ export { Collapsible, CollapsibleContent, CollapsibleTrigger }
@@ -122,6 +122,7 @@ const ComboxGroup = React.forwardRef<
122
122
  )
123
123
  })
124
124
  ComboxGroup.displayName = 'ComboxGroup'
125
+
125
126
  export { ComboxGroup }
126
127
 
127
128
  type ComboboxItemProps<T extends ComboboxItemType> = Omit<
@@ -29,8 +29,9 @@ const CommandInput = React.forwardRef<
29
29
  React.ComponentRef<typeof CommandPrimitive.Input>,
30
30
  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> & {
31
31
  wrapperClassName?: string
32
+ children?: React.ReactNode
32
33
  }
33
- >(({ className, wrapperClassName, placeholder = 'Search...', autoFocus = false, ...props }, ref) => (
34
+ >(({ className, wrapperClassName, placeholder = 'Search...', autoFocus = false, children, ...props }, ref) => (
34
35
  <div className={cn('mb-2 flex items-center gap-2 border-b px-1', wrapperClassName)} data-slot="command-input">
35
36
  <Search aria-hidden="true" className="size-5 shrink-0 opacity-50" />
36
37
  <CommandPrimitive.Input
@@ -44,6 +45,7 @@ const CommandInput = React.forwardRef<
44
45
  // tabIndex={0}
45
46
  {...props}
46
47
  />
48
+ {children}
47
49
  </div>
48
50
  ))
49
51
  CommandInput.displayName = CommandPrimitive.Input.displayName
@@ -161,11 +163,14 @@ CommandShortcut.displayName = 'CommandShortcut'
161
163
  function CommandDialog({
162
164
  children,
163
165
  shouldFilter,
166
+ contentClassName,
164
167
  ...props
165
- }: React.ComponentPropsWithoutRef<typeof Dialog> & { shouldFilter?: boolean }) {
168
+ }: React.ComponentPropsWithoutRef<typeof Dialog> & { shouldFilter?: boolean; contentClassName?: string }) {
166
169
  return (
167
170
  <Dialog {...props}>
168
- <DialogContent className="h-125 max-w-full p-0 lg:w-[700px]">
171
+ <DialogContent
172
+ className={cn('h-125 max-w-full p-0 transition-all duration-200 lg:w-[700px]', contentClassName)}
173
+ hideClose>
169
174
  <DialogTitle className="sr-only">Command palette</DialogTitle>
170
175
  <DialogDescription className="sr-only">Search for commands and navigation items</DialogDescription>
171
176
  <Command className="max-w-full" shouldFilter={shouldFilter}>
@@ -183,13 +188,13 @@ function useCommandListContext(__scopeCommand: Parameters<typeof CommandPrimitiv
183
188
 
184
189
  export {
185
190
  Command,
186
- CommandInput,
187
- CommandList,
191
+ CommandDialog,
192
+ CommandEmpty,
188
193
  CommandGroup,
194
+ CommandInput,
189
195
  CommandItem,
190
- CommandEmpty,
191
- CommandShortcut,
196
+ CommandList,
192
197
  CommandSeparator,
193
- CommandDialog,
198
+ CommandShortcut,
194
199
  useCommandListContext,
195
200
  }
@@ -169,18 +169,18 @@ ContextMenuShortcut.displayName = 'ContextMenuShortcut'
169
169
 
170
170
  export {
171
171
  ContextMenu,
172
- ContextMenuTrigger,
172
+ ContextMenuCheckboxItem,
173
173
  ContextMenuContent,
174
+ ContextMenuGroup,
174
175
  ContextMenuItem,
175
- ContextMenuCheckboxItem,
176
- ContextMenuRadioItem,
177
176
  ContextMenuLabel,
177
+ ContextMenuPortal,
178
+ ContextMenuRadioGroup,
179
+ ContextMenuRadioItem,
178
180
  ContextMenuSeparator,
179
181
  ContextMenuShortcut,
180
- ContextMenuGroup,
181
- ContextMenuPortal,
182
182
  ContextMenuSub,
183
183
  ContextMenuSubContent,
184
184
  ContextMenuSubTrigger,
185
- ContextMenuRadioGroup,
185
+ ContextMenuTrigger,
186
186
  }
@@ -137,12 +137,12 @@ DialogDescriptionResponsive.displayName = 'DialogDescriptionResponsive'
137
137
  DialogCloseResponsive.displayName = 'DialogCloseResponsive'
138
138
 
139
139
  export {
140
- DialogResponsive,
141
- DialogTriggerResponsive,
140
+ DialogCloseResponsive,
142
141
  DialogContentResponsive,
143
- DialogHeaderResponsive,
142
+ DialogDescriptionResponsive,
144
143
  DialogFooterResponsive,
144
+ DialogHeaderResponsive,
145
+ DialogResponsive,
145
146
  DialogTitleResponsive,
146
- DialogDescriptionResponsive,
147
- DialogCloseResponsive,
147
+ DialogTriggerResponsive,
148
148
  }
@@ -35,8 +35,8 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
35
35
 
36
36
  const DialogContent = React.forwardRef<
37
37
  React.ComponentRef<typeof DialogPrimitive.Content>,
38
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { closeText?: string }
39
- >(({ className, children, closeText = 'Close', ...props }, ref) => (
38
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { closeText?: string; hideClose?: boolean }
39
+ >(({ className, children, closeText = 'Close', hideClose = false, ...props }, ref) => (
40
40
  <DialogPortal>
41
41
  <DialogOverlay />
42
42
  <DialogPrimitive.Content
@@ -48,10 +48,12 @@ const DialogContent = React.forwardRef<
48
48
  )}
49
49
  {...props}>
50
50
  {children}
51
- <DialogPrimitive.Close className="absolute end-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-accent data-[state=open]:text-muted-foreground">
52
- <X aria-hidden="true" className="h-4 w-4" />
53
- <span className="sr-only">{closeText}</span>
54
- </DialogPrimitive.Close>
51
+ {!hideClose && (
52
+ <DialogPrimitive.Close className="absolute end-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-accent data-[state=open]:text-muted-foreground">
53
+ <X aria-hidden="true" className="h-4 w-4" />
54
+ <span className="sr-only">{closeText}</span>
55
+ </DialogPrimitive.Close>
56
+ )}
55
57
  </DialogPrimitive.Content>
56
58
  </DialogPortal>
57
59
  ))
@@ -93,13 +95,13 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName
93
95
 
94
96
  export {
95
97
  Dialog,
96
- DialogPortal,
97
- DialogOverlay,
98
- DialogTrigger,
99
98
  DialogClose,
100
99
  DialogContent,
101
- DialogHeader,
100
+ DialogDescription,
102
101
  DialogFooter,
102
+ DialogHeader,
103
+ DialogOverlay,
104
+ DialogPortal,
103
105
  DialogTitle,
104
- DialogDescription,
106
+ DialogTrigger,
105
107
  }
@@ -9,5 +9,6 @@ import {
9
9
  Provider,
10
10
  useDirection,
11
11
  } from '@gentleduck/primitives/direction'
12
+
12
13
  export type { Direction, DirectionProviderProps }
13
- export { useDirection, Provider, DirectionProvider, DirectionContext, DIRECTION_DICTIONARY }
14
+ export { DIRECTION_DICTIONARY, DirectionContext, DirectionProvider, Provider, useDirection }
@@ -173,13 +173,13 @@ DrawerDescription.displayName = 'DrawerDescription'
173
173
 
174
174
  export {
175
175
  Drawer,
176
- DrawerPortal,
177
- DrawerOverlay,
178
- DrawerTrigger,
179
176
  DrawerClose,
180
177
  DrawerContent,
181
- DrawerHeader,
178
+ DrawerDescription,
182
179
  DrawerFooter,
180
+ DrawerHeader,
181
+ DrawerOverlay,
182
+ DrawerPortal,
183
183
  DrawerTitle,
184
- DrawerDescription,
184
+ DrawerTrigger,
185
185
  }
@@ -172,18 +172,18 @@ DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
172
172
 
173
173
  export {
174
174
  DropdownMenu,
175
- DropdownMenuTrigger,
175
+ DropdownMenuCheckboxItem,
176
176
  DropdownMenuContent,
177
+ DropdownMenuGroup,
177
178
  DropdownMenuItem,
178
- DropdownMenuCheckboxItem,
179
- DropdownMenuRadioItem,
180
179
  DropdownMenuLabel,
180
+ DropdownMenuPortal,
181
+ DropdownMenuRadioGroup,
182
+ DropdownMenuRadioItem,
181
183
  DropdownMenuSeparator,
182
184
  DropdownMenuShortcut,
183
- DropdownMenuGroup,
184
- DropdownMenuPortal,
185
185
  DropdownMenuSub,
186
186
  DropdownMenuSubContent,
187
187
  DropdownMenuSubTrigger,
188
- DropdownMenuRadioGroup,
188
+ DropdownMenuTrigger,
189
189
  }
@@ -83,4 +83,4 @@ const EmptyContent = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'
83
83
  })
84
84
  EmptyContent.displayName = 'EmptyContent'
85
85
 
86
- export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia }
86
+ export { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle }
@@ -220,13 +220,13 @@ FieldError.displayName = 'FieldError'
220
220
 
221
221
  export {
222
222
  Field,
223
- FieldLabel,
223
+ FieldContent,
224
224
  FieldDescription,
225
225
  FieldError,
226
226
  FieldGroup,
227
+ FieldLabel,
227
228
  FieldLegend,
228
229
  FieldSeparator,
229
230
  FieldSet,
230
- FieldContent,
231
231
  FieldTitle,
232
232
  }
@@ -76,4 +76,4 @@ const HoverCardContent = React.forwardRef<
76
76
  })
77
77
  HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
78
78
 
79
- export { HoverCard, HoverCardTrigger, HoverCardContent }
79
+ export { HoverCard, HoverCardContent, HoverCardTrigger }
@@ -170,4 +170,4 @@ const InputGroupTextarea = React.forwardRef<HTMLTextAreaElement, React.Component
170
170
  )
171
171
  InputGroupTextarea.displayName = 'InputGroupTextarea'
172
172
 
173
- export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupInput, InputGroupTextarea }
173
+ export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea }
@@ -63,4 +63,4 @@ InputOTPSeparator.displayName = 'InputOTPSeparator'
63
63
  const REGEXP_ONLY_DIGITS_AND_CHARS = InputOTPPrimitive.REGEXP_ONLY_DIGITS_AND_CHARS
64
64
  const REGEXP_ONLY_DIGITS = InputOTPPrimitive.REGEXP_ONLY_DIGITS
65
65
 
66
- export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator, REGEXP_ONLY_DIGITS_AND_CHARS, REGEXP_ONLY_DIGITS }
66
+ export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS }
package/src/item/item.tsx CHANGED
@@ -174,13 +174,13 @@ ItemFooter.displayName = 'ItemFooter'
174
174
 
175
175
  export {
176
176
  Item,
177
- ItemMedia,
178
- ItemContent,
179
177
  ItemActions,
178
+ ItemContent,
179
+ ItemDescription,
180
+ ItemFooter,
180
181
  ItemGroup,
182
+ ItemHeader,
183
+ ItemMedia,
181
184
  ItemSeparator,
182
185
  ItemTitle,
183
- ItemDescription,
184
- ItemHeader,
185
- ItemFooter,
186
186
  }
@@ -202,19 +202,19 @@ MenubarShortcut.displayName = 'MenubarShortcut'
202
202
 
203
203
  export {
204
204
  Menubar,
205
- MenubarMenu,
206
- MenubarTrigger,
205
+ MenubarCheckboxItem,
207
206
  MenubarContent,
207
+ MenubarGroup,
208
208
  MenubarItem,
209
- MenubarSeparator,
210
209
  MenubarLabel,
211
- MenubarCheckboxItem,
210
+ MenubarMenu,
211
+ MenubarPortal,
212
212
  MenubarRadioGroup,
213
213
  MenubarRadioItem,
214
- MenubarPortal,
214
+ MenubarSeparator,
215
+ MenubarShortcut,
216
+ MenubarSub,
215
217
  MenubarSubContent,
216
218
  MenubarSubTrigger,
217
- MenubarGroup,
218
- MenubarSub,
219
- MenubarShortcut,
219
+ MenubarTrigger,
220
220
  }
@@ -141,12 +141,12 @@ NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayN
141
141
 
142
142
  export {
143
143
  NavigationMenu,
144
- NavigationMenuList,
145
- NavigationMenuItem,
146
144
  NavigationMenuContent,
147
- NavigationMenuTrigger,
148
- NavigationMenuLink,
149
145
  NavigationMenuIndicator,
146
+ NavigationMenuItem,
147
+ NavigationMenuLink,
148
+ NavigationMenuList,
149
+ NavigationMenuTrigger,
150
150
  NavigationMenuViewport,
151
151
  navigationMenuTriggerStyle,
152
152
  }
@@ -0,0 +1,35 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import * as React from 'react'
3
+ import { renderToStaticMarkup } from 'react-dom/server'
4
+ import { PaginationWrapper } from '../pagination'
5
+
6
+ describe('registry-ui pagination', () => {
7
+ test('PaginationWrapper prefers provided button icons over the default chevrons', () => {
8
+ const html = renderToStaticMarkup(
9
+ <PaginationWrapper
10
+ left={{ icon: <span data-icon="left">L</span> }}
11
+ maxLeft={{ icon: <span data-icon="max-left">ML</span> }}
12
+ right={{ icon: <span data-icon="right">R</span> }}
13
+ maxRight={{ icon: <span data-icon="max-right">MR</span> }}
14
+ />,
15
+ )
16
+
17
+ expect(html).toContain('data-icon="left"')
18
+ expect(html).toContain('data-icon="max-left"')
19
+ expect(html).toContain('data-icon="right"')
20
+ expect(html).toContain('data-icon="max-right"')
21
+ expect(html).not.toContain('lucide-chevron-left')
22
+ expect(html).not.toContain('lucide-chevron-right')
23
+ expect(html).not.toContain('lucide-chevrons-left')
24
+ expect(html).not.toContain('lucide-chevrons-right')
25
+ })
26
+
27
+ test('PaginationWrapper still renders default chevrons when icons are not provided', () => {
28
+ const html = renderToStaticMarkup(<PaginationWrapper />)
29
+
30
+ expect(html).toContain('lucide-chevron-left')
31
+ expect(html).toContain('lucide-chevron-right')
32
+ expect(html).toContain('lucide-chevrons-left')
33
+ expect(html).toContain('lucide-chevrons-right')
34
+ })
35
+ })
@@ -121,10 +121,10 @@ const PaginationWrapper = (props: DuckPaginationProps) => {
121
121
  const { className: wrapperClassName, dir, ...wrapperProps } = props.wrapper ?? {}
122
122
  const { className: contentClassName, ...contentProps } = props.content ?? {}
123
123
  const { className: itemClassName, ...itemProps } = props.item ?? {}
124
- const { className: rightClassName, ...rightProps } = props.right ?? {}
125
- const { className: maxRightClassName, ...maxRightProps } = props.maxRight ?? {}
126
- const { className: leftClassName, ...leftProps } = props.left ?? {}
127
- const { className: maxLeftClassName, ...maxLeftProps } = props.maxLeft ?? {}
124
+ const { className: rightClassName, icon: rightIcon, ...rightProps } = props.right ?? {}
125
+ const { className: maxRightClassName, icon: maxRightIcon, ...maxRightProps } = props.maxRight ?? {}
126
+ const { className: leftClassName, icon: leftIcon, ...leftProps } = props.left ?? {}
127
+ const { className: maxLeftClassName, icon: maxLeftIcon, ...maxLeftProps } = props.maxLeft ?? {}
128
128
  const direction = useDirection(dir as Direction)
129
129
  const StartIcon = direction === 'rtl' ? ChevronRightIcon : ChevronLeftIcon
130
130
  const EndIcon = direction === 'rtl' ? ChevronLeftIcon : ChevronRightIcon
@@ -138,41 +138,41 @@ const PaginationWrapper = (props: DuckPaginationProps) => {
138
138
  <Button
139
139
  aria-label="Go to first page"
140
140
  className={cn('w-[32px] p-0', maxLeftClassName)}
141
+ icon={maxLeftIcon === undefined ? <StartDoubleIcon aria-hidden="true" /> : maxLeftIcon}
141
142
  size="sm"
142
143
  variant="outline"
143
- {...maxLeftProps}>
144
- <StartDoubleIcon aria-hidden="true" />
145
- </Button>
144
+ {...maxLeftProps}
145
+ />
146
146
  </PaginationItem>
147
147
  <PaginationItem className={cn(itemClassName)} {...itemProps}>
148
148
  <Button
149
149
  aria-label="Go to previous page"
150
150
  className={cn('w-[32px] p-0', leftClassName)}
151
+ icon={leftIcon === undefined ? <StartIcon aria-hidden="true" /> : leftIcon}
151
152
  size="sm"
152
153
  variant="outline"
153
- {...leftProps}>
154
- <StartIcon aria-hidden="true" />
155
- </Button>
154
+ {...leftProps}
155
+ />
156
156
  </PaginationItem>
157
157
  <PaginationItem className={cn(itemClassName)} {...itemProps}>
158
158
  <Button
159
159
  aria-label="Go to next page"
160
160
  className={cn('w-[32px] p-0', rightClassName)}
161
+ icon={rightIcon === undefined ? <EndIcon aria-hidden="true" /> : rightIcon}
161
162
  size="sm"
162
163
  variant="outline"
163
- {...rightProps}>
164
- <EndIcon aria-hidden="true" />
165
- </Button>
164
+ {...rightProps}
165
+ />
166
166
  </PaginationItem>
167
167
  <PaginationItem className={cn(itemClassName)} {...itemProps}>
168
168
  <Button
169
169
  aria-label="Go to last page"
170
170
  className={cn('w-[32px] p-0', maxRightClassName)}
171
+ icon={maxRightIcon === undefined ? <EndDoubleIcon aria-hidden="true" /> : maxRightIcon}
171
172
  size="sm"
172
173
  variant="outline"
173
- {...maxRightProps}>
174
- <EndDoubleIcon aria-hidden="true" />
175
- </Button>
174
+ {...maxRightProps}
175
+ />
176
176
  </PaginationItem>
177
177
  </PaginationContent>
178
178
  </Pagination>
@@ -36,4 +36,4 @@ PopoverContent.displayName = PopoverPrimitive.Content.displayName
36
36
  export const PopoverClose: typeof PopoverPrimitive.Close = PopoverPrimitive.Close
37
37
  PopoverClose.displayName = 'PopoverClose'
38
38
 
39
- export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
39
+ export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger }
@@ -51,4 +51,4 @@ const ResizableHandle = React.forwardRef<
51
51
  ))
52
52
  ResizableHandle.displayName = 'ResizableHandle'
53
53
 
54
- export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
54
+ export { ResizableHandle, ResizablePanel, ResizablePanelGroup }
@@ -131,13 +131,13 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName
131
131
 
132
132
  export {
133
133
  Select,
134
- SelectGroup,
135
- SelectValue,
136
- SelectTrigger,
137
134
  SelectContent,
138
- SelectLabel,
135
+ SelectGroup,
139
136
  SelectItem,
140
- SelectSeparator,
141
- SelectScrollUpButton,
137
+ SelectLabel,
142
138
  SelectScrollDownButton,
139
+ SelectScrollUpButton,
140
+ SelectSeparator,
141
+ SelectTrigger,
142
+ SelectValue,
143
143
  }
@@ -92,13 +92,13 @@ SheetDescription.displayName = SheetPrimitive.Description.displayName
92
92
 
93
93
  export {
94
94
  Sheet,
95
- SheetPortal,
96
- SheetOverlay,
97
- SheetTrigger,
98
95
  SheetClose,
99
96
  SheetContent,
100
- SheetHeader,
97
+ SheetDescription,
101
98
  SheetFooter,
99
+ SheetHeader,
100
+ SheetOverlay,
101
+ SheetPortal,
102
102
  SheetTitle,
103
- SheetDescription,
103
+ SheetTrigger,
104
104
  }
@@ -0,0 +1,39 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import * as React from 'react'
3
+ import { renderToStaticMarkup } from 'react-dom/server'
4
+ import { SidebarTrigger } from '../sidebar'
5
+ import { SidebarContext } from '../sidebar.hooks'
6
+
7
+ const sidebarContextValue = {
8
+ state: 'expanded' as const,
9
+ open: true,
10
+ setOpen: () => {},
11
+ openMobile: false,
12
+ setOpenMobile: () => {},
13
+ isMobile: false,
14
+ toggleSidebar: () => {},
15
+ dir: 'ltr' as const,
16
+ }
17
+
18
+ describe('registry-ui sidebar', () => {
19
+ test('SidebarTrigger prefers a provided icon over the default panel icon', () => {
20
+ const html = renderToStaticMarkup(
21
+ <SidebarContext.Provider value={sidebarContextValue}>
22
+ <SidebarTrigger icon={<span data-icon="custom">C</span>} />
23
+ </SidebarContext.Provider>,
24
+ )
25
+
26
+ expect(html).toContain('data-icon="custom"')
27
+ expect(html).not.toContain('lucide-panel-left')
28
+ })
29
+
30
+ test('SidebarTrigger still renders the default panel icon when no icon is provided', () => {
31
+ const html = renderToStaticMarkup(
32
+ <SidebarContext.Provider value={sidebarContextValue}>
33
+ <SidebarTrigger />
34
+ </SidebarContext.Provider>,
35
+ )
36
+
37
+ expect(html).toContain('lucide-panel-left')
38
+ })
39
+ })
@@ -220,7 +220,7 @@ Sidebar.displayName = 'Sidebar'
220
220
  const SidebarTrigger = React.forwardRef<
221
221
  React.ComponentRef<typeof Button>,
222
222
  React.ComponentPropsWithoutRef<typeof Button> & { text?: string }
223
- >(({ className, onClick, text = 'Toggle Sidebar', ...props }, ref) => {
223
+ >(({ className, icon, onClick, text = 'Toggle Sidebar', ...props }, ref) => {
224
224
  const { toggleSidebar } = useSidebar()
225
225
  const direction = useDirection()
226
226
 
@@ -232,13 +232,13 @@ const SidebarTrigger = React.forwardRef<
232
232
  variant="ghost"
233
233
  size="icon-sm"
234
234
  dir={direction}
235
+ icon={icon === undefined ? <PanelLeftIcon aria-hidden="true" className="rtl:-scale-x-100" /> : icon}
235
236
  className={cn(className)}
236
237
  onClick={(event) => {
237
238
  onClick?.(event)
238
239
  toggleSidebar()
239
240
  }}
240
241
  {...props}>
241
- <PanelLeftIcon aria-hidden="true" className="rtl:-scale-x-100" />
242
242
  <span className="sr-only">{text}</span>
243
243
  </Button>
244
244
  )
@@ -92,4 +92,4 @@ const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttribu
92
92
  )
93
93
  TableCaption.displayName = 'TableCaption'
94
94
 
95
- export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption }
95
+ export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow }
package/src/tabs/tabs.tsx CHANGED
@@ -148,4 +148,4 @@ const TabsContent = React.forwardRef<
148
148
  })
149
149
  TabsContent.displayName = 'TabsContent'
150
150
 
151
- export { Tabs, TabsList, TabsTrigger, TabsContent }
151
+ export { Tabs, TabsContent, TabsList, TabsTrigger }
@@ -32,4 +32,4 @@ const TooltipContent = React.forwardRef<
32
32
  ))
33
33
  TooltipContent.displayName = TooltipPrimitive.Content.displayName
34
34
 
35
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
35
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }