Skip to content

Commit 32d202a

Browse files
committed
fix(ui): forward refs in Button so Radix asChild slots work
The custom Button was a plain function component, so when used as a child of `<DropdownMenu.Trigger asChild>` the ref Radix Slot composed onto it never reached the underlying `<button>` DOM node. Radix's PopperAnchor ended up with a null anchor, Floating UI's computePosition never ran, and the CopyPageDropdown wrapper stayed at the off-screen fallback `translate(0px, -200%)` so the menu was invisible. Wrap Button in React.forwardRef and pass the ref through to createElement; switch ButtonProps to ComponentPropsWithRef so `ref={}` type-checks for consumers.
1 parent 231bc91 commit 32d202a

1 file changed

Lines changed: 20 additions & 16 deletions

File tree

src/ui/Button.tsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,7 @@ type ButtonOwnProps<TElement extends React.ElementType = 'button'> = {
3030

3131
type ButtonProps<TElement extends React.ElementType = 'button'> =
3232
ButtonOwnProps<TElement> &
33-
Omit<
34-
React.ComponentPropsWithoutRef<TElement>,
35-
keyof ButtonOwnProps<TElement>
36-
>
33+
Omit<React.ComponentPropsWithRef<TElement>, keyof ButtonOwnProps<TElement>>
3734

3835
type ButtonComponent = <TElement extends React.ElementType = 'button'>(
3936
props: ButtonProps<TElement>,
@@ -102,16 +99,22 @@ function getDefaultRounded(size: ButtonSize): ButtonRounded {
10299
return 'lg'
103100
}
104101

105-
export const Button: ButtonComponent = ({
106-
as,
107-
children,
108-
variant = 'primary',
109-
color = 'blue',
110-
size,
111-
rounded,
112-
className,
113-
...props
114-
}) => {
102+
type ButtonInnerProps = ButtonOwnProps & Record<string, unknown>
103+
104+
export const Button: ButtonComponent = React.forwardRef<
105+
HTMLElement,
106+
ButtonInnerProps
107+
>(function Button(props, ref) {
108+
const {
109+
as,
110+
children,
111+
variant = 'primary',
112+
color = 'blue',
113+
size,
114+
rounded,
115+
className,
116+
...rest
117+
} = props as ButtonOwnProps & Record<string, unknown>
115118
const Component = as || 'button'
116119
const resolvedSize = size ?? getDefaultSize(variant)
117120
const resolvedRounded = rounded ?? getDefaultRounded(resolvedSize)
@@ -126,6 +129,7 @@ export const Button: ButtonComponent = ({
126129
return React.createElement(
127130
Component,
128131
{
132+
ref,
129133
className: twMerge(
130134
baseStyles,
131135
variantStyles[variant],
@@ -134,8 +138,8 @@ export const Button: ButtonComponent = ({
134138
colorStyles,
135139
className,
136140
),
137-
...props,
141+
...rest,
138142
},
139143
children,
140144
)
141-
}
145+
}) as unknown as ButtonComponent

0 commit comments

Comments
 (0)