diff --git a/packages/components/src/Button/Button.tsx b/packages/components/src/Button/Button.tsx index ed64fa31be..edf95fea0d 100644 --- a/packages/components/src/Button/Button.tsx +++ b/packages/components/src/Button/Button.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { forwardRef } from "react"; import { Link } from "react-router-dom"; import classnames from "classnames"; import { type ButtonProps, type HTMLButtonType } from "./Button.types"; @@ -7,17 +7,10 @@ import { useButtonStyles } from "./useButtonStyles"; import { ButtonContent, ButtonIcon, ButtonLabel } from "./ButtonInternals"; import { ButtonProvider } from "./ButtonProvider"; -function Button(props: ButtonProps) { - const { size } = props; - - return ( - - - - ); -} - -function ButtonWrapper(props: ButtonProps) { +const ButtonWrapper = forwardRef< + HTMLButtonElement | HTMLAnchorElement, + ButtonProps +>((props, ref) => { const { ariaControls, ariaHaspopup, @@ -72,7 +65,7 @@ function ButtonWrapper(props: ButtonProps) { if (to) { return ( - + }> {buttonInternals} ); @@ -80,10 +73,65 @@ function ButtonWrapper(props: ButtonProps) { const Tag = url ? "a" : "button"; - return {buttonInternals}; -} + // Use createElement for proper ref typing (similar to Chip component) + return React.createElement(Tag, { ...tagProps, ref }, buttonInternals); +}); + +ButtonWrapper.displayName = "ButtonWrapper"; +const ButtonForwarded = forwardRef< + HTMLButtonElement | HTMLAnchorElement, + ButtonProps +>((props, ref) => { + const { size } = props; + + return ( + + + + ); +}); + +ButtonForwarded.displayName = "Button"; + +// Add function overloads for type-safe refs +export const Button = ButtonForwarded as unknown as { + // Overload for button (no url, no to) + ( + props: ButtonProps & { + url?: never; + to?: never; + ref?: React.Ref; + }, + ): ReturnType; + + // Overload for anchor (with url, no to) + ( + props: ButtonProps & { + url: string; + to?: never; + ref?: React.Ref; + }, + ): ReturnType; + + // Overload for Link (with to, no url) + ( + props: ButtonProps & { + to: string; + url?: never; + ref?: React.Ref; + }, + ): ReturnType; + + // Fallback for when ref is not provided (union type) + (props: ButtonProps): ReturnType; +} & { + Label: typeof ButtonLabel; + Icon: typeof ButtonIcon; +}; + +// Attach namespace components (preserving existing API) Button.Label = ButtonLabel; Button.Icon = ButtonIcon; + export type { ButtonProps }; -export { Button };