Skip to content

Commit

Permalink
fix: button typing for button/anchors, fixed styling of buttons with …
Browse files Browse the repository at this point in the history
…certain variants
  • Loading branch information
christianblandford committed Mar 18, 2022
1 parent 12b08b5 commit 0af0ed2
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 35 deletions.
16 changes: 16 additions & 0 deletions src/Button/Button.stories.tsx
Expand Up @@ -101,3 +101,19 @@ export const Icons: Story<ButtonProps> = (args) => {
</div>
)
}

export const AsHref: Story<ButtonProps> = (args) => {
return (
<div className="flex gap-x-2">
<Button
{...args}
onClick={() => alert('See, I have an onClick event and no href.')}
>
I'm a {`<button>`}
</Button>
<Button {...args} href="https://google.com" target="_blank">
I'm an {`<a>`}
</Button>
</div>
)
}
96 changes: 61 additions & 35 deletions src/Button/Button.tsx
Expand Up @@ -9,30 +9,41 @@ import {
ComponentSize,
} from '../types'

export type ButtonProps = Omit<
React.ButtonHTMLAttributes<HTMLButtonElement>,
'color'
> &
IComponentBaseProps & {
href?: string
shape?: ComponentShape
size?: ComponentSize
variant?: 'outline' | 'link'
color?: ComponentColor
fullWidth?: boolean
responsive?: boolean
animation?: boolean
loading?: boolean
active?: boolean
startIcon?: ReactNode
endIcon?: ReactNode
}
type ButtonBaseProps = IComponentBaseProps & {
shape?: ComponentShape
size?: ComponentSize
variant?: 'outline' | 'link'
color?: ComponentColor
fullWidth?: boolean
responsive?: boolean
animation?: boolean
loading?: boolean
active?: boolean
startIcon?: ReactNode
endIcon?: ReactNode
disabled?: boolean
}

// Allow for proper typing when button is rendered as an anchor tag or a button tag

type ButtonElProps = ButtonBaseProps &
React.ButtonHTMLAttributes<HTMLButtonElement> & { href?: never }

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
type AnchorElProps = ButtonBaseProps &
React.AnchorHTMLAttributes<HTMLAnchorElement>

type PolymorphicButton = {
(props: AnchorElProps): JSX.Element
(props: ButtonElProps): JSX.Element
}

const Button = forwardRef<
HTMLButtonElement | HTMLAnchorElement,
ButtonElProps | AnchorElProps
>(
(
{
children,
href,
shape,
size,
variant,
Expand All @@ -52,42 +63,59 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
},
ref
): JSX.Element => {
const classes = twMerge(
const baseClasses = twMerge(
'btn',
className,
clsx(
((startIcon && !loading) || endIcon) && 'gap-2',
{
clsx(((startIcon && !loading) || endIcon) && 'gap-2', {
[`btn-${size}`]: size,
[`btn-${shape}`]: shape,
[`btn-${variant}`]: variant,
[`btn-${color}`]: color,
'btn-block': fullWidth,
'btn-xs md:btn-sm lg:btn-md xl:btn-lg': responsive,
'no-animation': !animation,
'btn-active': active,
'btn-disabled': disabled,
'btn-active': active && !disabled, // !disabled only matters when rendering an active, disabled button as <a>...
loading: loading,
})
)

if (href) {
if (props.href) {
// Handle disabled outline buttons differently when they are an anchor.
// <a> tags do not support the disabled attribute, so instead we apply the same classes
// that the disabled psuedotag applies to a regular button.
const classes = twMerge(
baseClasses,
disabled && 'btn-disabled',
variant === 'outline' &&
(disabled ? 'border border-current' : 'btn-outline'),
variant === 'link' && !disabled && 'btn-link'
)
return (
<a
{...props}
data-theme={dataTheme}
className={classes}
style={style}
href={href}
href={props.href}
ref={ref as React.ForwardedRef<HTMLAnchorElement>}
>
{startIcon && startIcon}
{children}
{endIcon && endIcon}
</a>
)
} else {
const classes = twMerge(
baseClasses,
disabled && 'btn-disabled',
clsx({
[`btn-${variant}`]: variant,
})
)

return (
<button
{...props}
ref={ref}
{...(props as ButtonElProps)}
ref={ref as React.ForwardedRef<HTMLButtonElement>}
data-theme={dataTheme}
className={classes}
style={style}
Expand All @@ -100,8 +128,6 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
)
}
}
)

Button.displayName = 'Button'
) as PolymorphicButton

export default Button
export default Object.assign(Button, { displayName: 'Button' })

0 comments on commit 0af0ed2

Please sign in to comment.