Skip to content

Commit

Permalink
Use <Slot /> to merge props and render link buttons as <Link /> (#24
Browse files Browse the repository at this point in the history
)

* fix: render `Link` for link buttons

* docs: correct "Link" section in "Button" docs

* fix: make socials `newTab` and disable correctly

* fix: remove underline

Co-authored-by: Shun Kakinoki <[email protected]>
  • Loading branch information
fiveoutofnine and shunkakinoki authored Nov 12, 2023
1 parent a04dd54 commit 7029233
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 21 deletions.
9 changes: 7 additions & 2 deletions components/pages/home/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,15 @@ const FiveoutofnineHeader: FC = () => {

{/* Links (desktop) */}
<div className="hidden space-x-2 md:flex">
<Button intent="primary" href="https://twitter.com/fiveoutofnine" leftIcon={<Twitter />}>
<Button
intent="primary"
href="https://twitter.com/fiveoutofnine"
leftIcon={<Twitter />}
newTab
>
Twitter
</Button>
<Button href="https://github.com/fiveoutofnine" leftIcon={<Github />}>
<Button href="https://github.com/fiveoutofnine" leftIcon={<Github />} newTab>
GitHub
</Button>
</div>
Expand Down
21 changes: 12 additions & 9 deletions components/ui/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type ForwardedRef, forwardRef } from 'react';

import { buttonIconVariants, buttonVariants } from './styles';
import type { ButtonProps } from './types';
import { Slot } from '@radix-ui/react-slot';
import { cx } from 'class-variance-authority';
import { twMerge } from 'tailwind-merge';

Expand All @@ -18,8 +19,6 @@ const Button = forwardRef(
leftIcon,
rightIcon,
newTab,
title,
onClick,
children,
...rest
}: ButtonProps,
Expand All @@ -32,29 +31,33 @@ const Button = forwardRef(
className,
),
),
title: title || href || undefined,
'data-variant': variant,
'data-disabled': disabled,
'aria-disabled': disabled,
disabled,
ref,
onClick: newTab && href ? () => window.open(href, '_blank') : onClick,
...rest,
};

if (href && !newTab) {
// Destructure `ref` from `props: JSX.IntrinsicElements['button']`, so the
// remaining props are type-compatible with `<Link />` for the `<Slot />`
// component to merge in.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { ref: _, ...restWithoutRef } = props;

if (href && !disabled) {
return (
<Link href={href} passHref legacyBehavior>
<button {...props}>
<Slot ref={ref} {...restWithoutRef}>
<Link href={href} {...(newTab ? { target: '_blank', rel: 'noopener noreferrer' } : {})}>
{leftIcon && variant !== 'text' ? (
<span className={buttonIconVariants({ size })}>{leftIcon}</span>
) : null}
<span>{children}</span>
{rightIcon && variant !== 'text' ? (
<span className={buttonIconVariants({ size })}>{rightIcon}</span>
) : null}
</button>
</Link>
</Link>
</Slot>
);
}

Expand Down
1 change: 1 addition & 0 deletions components/ui/button/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const buttonVariants = cva(
'justify-center',
'items-center',
'font-medium',
'no-underline',
'transition-colors',
'focus-visible:outline-none',
'focus-visible:ring-2',
Expand Down
21 changes: 12 additions & 9 deletions components/ui/icon-button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type ForwardedRef, forwardRef } from 'react';

import { iconButtonIconVariants, iconButtonVariants } from './styles';
import type { IconButtonProps } from './types';
import { Slot } from '@radix-ui/react-slot';
import { cx } from 'class-variance-authority';
import { twMerge } from 'tailwind-merge';

Expand All @@ -16,8 +17,6 @@ const IconButton = forwardRef(
disabled = false,
href,
newTab,
title,
onClick,
children,
...rest
}: IconButtonProps,
Expand All @@ -30,23 +29,27 @@ const IconButton = forwardRef(
className,
),
),
title: title || href || undefined,
'data-variant': variant,
'data-disabled': disabled,
'aria-disabled': disabled,
disabled,
ref,
onClick: newTab && href ? () => window.open(href, '_blank') : onClick,
...rest,
};

if (href && !newTab) {
// Destructure `ref` from `props: JSX.IntrinsicElements['button']`, so the
// remaining props are type-compatible with `<Link />` for the `<Slot />`
// component to merge in.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { ref: _, ...restWithoutRef } = props;

if (href && !disabled) {
return (
<Link href={href} passHref legacyBehavior>
<button {...props}>
<Slot ref={ref} {...restWithoutRef}>
<Link href={href} {...(newTab ? { target: '_blank', rel: 'noopener noreferrer' } : {})}>
<span className={iconButtonIconVariants({ size })}>{children}</span>
</button>
</Link>
</Link>
</Slot>
);
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-hover-card": "^1.0.6",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.3",
"@radix-ui/react-toast": "^1.1.3",
"@radix-ui/react-tooltip": "^1.0.5",
Expand Down
2 changes: 1 addition & 1 deletion pages/design/component/button.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ To use icon-only buttons, use `IconButton`. It is identical to `Button`, except

## Link

To have the button act as a link, pass in a URL to the `href` prop. To open the link in a new tab, use the `newTab` prop. Note that the final component is rendered as a `<button>` element, not `<a>`.
To have the button act as a link, pass in a URL to the `href` prop. To open the link in a new tab, use the `newTab` prop. Note that the final component will be rendered as a `<a>` element, so browser features like right-clicking to open in a new tab will work.

<DesignComponentsDisplay
className="grid-flow-col grid-rows-1"
Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 comment on commit 7029233

@vercel
Copy link

@vercel vercel bot commented on 7029233 Nov 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.