-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #116 from abusix/pla-1066-split-link-and-button-in…
…to-two-separate-components BREAKING CHANGE: split button/link component and support asChild for links
- Loading branch information
Showing
8 changed files
with
174 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Link } from "./link"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import React from "react"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { Link } from "./link"; | ||
import { ChatIcon, DiagramTreeIcon, LockIcon } from "../../icons"; | ||
import { hiddenArgControl } from "../../util/storybook-utils"; | ||
|
||
const icons = { undefined, ChatIcon, DiagramTreeIcon, LockIcon }; | ||
const iconArg = { | ||
description: "Icon component", | ||
options: Object.keys(icons), | ||
mapping: icons, | ||
}; | ||
|
||
const meta: Meta<typeof Link> = { | ||
title: "Link", | ||
component: Link, | ||
args: { | ||
children: "Link Label", | ||
LeftIcon: undefined, | ||
RightIcon: undefined, | ||
}, | ||
argTypes: { | ||
LeftIcon: iconArg, | ||
RightIcon: iconArg, | ||
asChild: hiddenArgControl, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof Link>; | ||
|
||
export const Default: Story = { | ||
render: (args) => ( | ||
<Link href="https://www.google.de/" {...args} asChild={false}> | ||
{args.children} | ||
</Link> | ||
), | ||
}; | ||
|
||
export const AsChild: Story = { | ||
render: (args) => ( | ||
<Link {...args} asChild> | ||
<a href="https://www.google.de/">{args.children}</a> | ||
</Link> | ||
), | ||
}; | ||
|
||
export const WithChilds: Story = { | ||
argTypes: { | ||
children: hiddenArgControl, | ||
}, | ||
render: (args) => ( | ||
<Link href="https://www.google.de/" {...args} asChild={false}> | ||
<div> | ||
<span>Nested</span> | ||
<span>Elements</span> | ||
</div> | ||
</Link> | ||
), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React from "react"; | ||
import { AsChildProps, Slot } from "../slot/slot"; | ||
import { classNames } from "../../util/class-names"; | ||
|
||
const linkVariants = { | ||
primary: | ||
"bg-primary-500 text-neutral-0 hover:bg-primary-600 active:bg-primary-600 focus:ring-2 focus:ring-primary-200 focus:bg-primary-600 disabled:bg-primary-200 fill-neutral-0", | ||
secondary: | ||
"text-neutral-700 bg-neutral-0 border border-neutral-400 hover:border-neutral-600 hover:text-neutral-800 active:bg-neutral-100 focus:ring-2 focus:ring-primary-200 focus:text-neutral-800 disabled:text-neutral-500 disabled:border-neutral-300 disabled:bg-neutral-0 fill-neutral-0", | ||
minimal: | ||
"text-neutral-700 hover:bg-neutral-100 hover:text-neutral-800 active:bg-neutral-200 focus:ring-2 focus:ring-primary-200 focus:text-neutral-800 disabled:text-neutral-500 disabled:bg-neutral-0 fill-neutral-0", | ||
danger: "text-neutral-0 bg-danger-500 hover:bg-danger-500 active:bg-danger-700 focus:ring-2 focus:ring-danger-100 focus:bg-danger-600 disabled:bg-danger-100 fill-neutral-0", | ||
"danger-secondary": | ||
"bg-neutral-0 text-danger-500 border border-danger-400 hover:bg-danger-50 hover:text-danger-600 active:border-danger-700 active:text-danger-700 active:bg-danger-100 focus:ring-2 focus:ring-danger-100 focus:text-danger-600 disabled:border-danger-100 disabled:text-danger-100 disabled:bg-neutral-0 fill-danger-600 disabled:fill-danger-100", | ||
}; | ||
|
||
const iconVariants = { | ||
primary: "text-neutral-0", | ||
secondary: | ||
"fill-neutral-600 group-hover:text-neutral-700 group-focus:text-neutral-700 group-disabled:text-neutral-400", | ||
minimal: | ||
"fill-neutral-600 group-hover:text-neutral-700 group-focus:text-neutral-700 group-disabled:text-neutral-400", | ||
danger: "", | ||
"danger-secondary": "", | ||
}; | ||
|
||
type LinkProps = AsChildProps<React.AnchorHTMLAttributes<HTMLAnchorElement>> & { | ||
className?: string; | ||
variant?: keyof typeof linkVariants; | ||
LeftIcon?: React.ElementType; | ||
RightIcon?: React.ElementType; | ||
}; | ||
|
||
export const Link = ({ | ||
variant = "primary", | ||
className, | ||
children, | ||
LeftIcon, | ||
RightIcon, | ||
asChild = false, | ||
...props | ||
}: LinkProps) => { | ||
const Comp = asChild ? Slot : "a"; | ||
const commonClasses = classNames( | ||
`group flex h-8 items-center gap-2 whitespace-nowrap rounded px-4 text-xs font-semibold focus:outline-none disabled:cursor-not-allowed `, | ||
linkVariants[variant], | ||
className | ||
); | ||
|
||
if (React.isValidElement(children)) { | ||
return React.cloneElement(children, { | ||
...children.props, | ||
children: ( | ||
<> | ||
{LeftIcon ? <LeftIcon className={`${iconVariants[variant]} h-3 w-3`} /> : null} | ||
{children.props.children} | ||
{RightIcon ? ( | ||
<RightIcon className={`${iconVariants[variant]} h-3 w-3`} /> | ||
) : null} | ||
</> | ||
), | ||
className: commonClasses, | ||
}); | ||
} | ||
|
||
return ( | ||
<Comp {...props} className={commonClasses}> | ||
<> | ||
{LeftIcon ? <LeftIcon className={`${iconVariants[variant]} h-3 w-3`} /> : null} | ||
{children} | ||
{RightIcon ? <RightIcon className={`${iconVariants[variant]} h-3 w-3`} /> : null} | ||
</> | ||
</Comp> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import React from "react"; | ||
import { classNames } from "../../util/class-names"; | ||
|
||
export type AsChildProps<DefaultElementProps> = | ||
| ({ asChild?: false } & DefaultElementProps) | ||
| { asChild: true; children: React.ReactNode }; | ||
|
||
export const Slot = ({ | ||
children, | ||
...props | ||
}: React.HTMLAttributes<HTMLElement> & { | ||
children?: React.ReactNode; | ||
}) => { | ||
if (React.isValidElement(children)) { | ||
return React.cloneElement(children, { | ||
...props, | ||
...children.props, | ||
style: { | ||
...props.style, | ||
...children.props.style, | ||
}, | ||
className: classNames(props.className, props.className, children.props.className), | ||
}); | ||
} | ||
|
||
if (React.Children.count(children) > 1) { | ||
React.Children.only(null); | ||
} | ||
|
||
return null; | ||
}; |