diff --git a/src/components/Link/link.stories.tsx b/src/components/Link/link.stories.tsx index f819e26ac6..a553f11dd0 100644 --- a/src/components/Link/link.stories.tsx +++ b/src/components/Link/link.stories.tsx @@ -7,6 +7,20 @@ const meta: Meta = { title: 'Components (Verified)/Links', tags: ['autodocs'], component: Link, + argTypes: { + href: { control: 'text' }, + label: { control: 'text' }, + isButton: { control: 'boolean' }, + isJump: { control: 'boolean' }, + isRouterLink: { control: 'boolean' }, + appearance: { + control: 'select', + options: ['primary', 'secondary', 'warning', 'destructive'], + }, + iconLeft: { control: 'text' }, + iconRight: { control: 'text' }, + children: { control: 'text' }, + }, }; export default meta; @@ -15,8 +29,34 @@ type Story = StoryObj; const DefaultArguments = { args: { - href: '#', - children: 'Link Text', + href: '/#', + label: 'Link Text', + isButton: false, + isJump: false, + isRouterLink: false, + appearance: 'primary', + iconLeft: undefined, + iconRight: undefined, + children: undefined, + }, +}; + +export const Configurable: Story = { + args: { + ...DefaultArguments.args, + }, + render: (arguments_) => + arguments_.isRouterLink ? ( + + + + ) : ( + + ), + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + const link = canvas.getByRole('link'); + await expect(link).toHaveAttribute('href', args.href); }, }; @@ -117,7 +157,9 @@ export const Destructive: Story = { args: { ...DefaultArguments.args, }, - render: () => , + render: () => ( + + ), }; export const LinkWithReactRouterLink: Story = { diff --git a/src/components/Link/link.test.tsx b/src/components/Link/link.test.tsx index dacb77da9e..a7f4944152 100644 --- a/src/components/Link/link.test.tsx +++ b/src/components/Link/link.test.tsx @@ -11,14 +11,14 @@ describe('', () => { }; const testId = linkBaseProperties['data-testid']; - it('Type: "default"', () => { + it('Appearance: "primary" (default)', () => { render(); const link = screen.getByTestId(testId); expect(link).toHaveAttribute('href', '/foo/bar'); }); - it('Type: "destructive"', () => { - render(); + it('Appearance: "warning" (destructive)', () => { + render(); const link = screen.getByTestId(testId); expect(link).toHaveClass('a-btn a-btn--link a-btn--warning'); }); @@ -73,6 +73,29 @@ describe('', () => { expect(await screen.findByTestId('link-icon-right')).toBeInTheDocument(); }); + it('Option: appearance - it applies button appearance classes when isButton', () => { + render( + , + ); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-btn'); + expect(link).toHaveClass('a-btn--secondary'); + }); + + it('Option: appearance - it applies warning class when isButton', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-btn'); + expect(link).toHaveClass('a-btn--warning'); + }); + + it('Option: appearance - it does not apply secondary class without isButton', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).not.toHaveClass('a-btn--secondary'); + expect(link).not.toHaveClass('a-btn--warning'); + }); + it('Other: propagates other attributes', () => { render(); const link = screen.getByTestId(testId); diff --git a/src/components/Link/link.tsx b/src/components/Link/link.tsx index fa86b4f17f..1714ed8540 100644 --- a/src/components/Link/link.tsx +++ b/src/components/Link/link.tsx @@ -13,6 +13,10 @@ export interface LinkProperties extends HTMLProps { * Whether the link should be rendered as a button. */ isButton?: boolean; + /** + * What is the link's appearance? + */ + appearance?: 'primary' | 'secondary' | 'destructive' | 'warning'; /** * Any children to render within the link. Allows you to wrap any node with anchor tag */ @@ -42,10 +46,6 @@ export interface LinkProperties extends HTMLProps { */ label?: string; ref?: Ref; - /** - * What type of link should be rendered - */ - type?: 'default' | 'destructive'; } /** @@ -56,6 +56,7 @@ export interface LinkProperties extends HTMLProps { */ export default function Link({ isButton = false, + appearance, children, href, iconLeft, @@ -63,7 +64,6 @@ export default function Link({ isJump = false, isRouterLink = false, label, - type = 'default', ...others }: LinkProperties): JSXElement { const hasLeftIcon = Boolean(iconLeft); @@ -72,10 +72,12 @@ export default function Link({ const shouldUseLinkStyles = !isButton && (hasIcons || isJump); const shouldWrapLabel = isButton || shouldUseLinkStyles; const labelNode = shouldWrapLabel ? {label} : label; + const isDestructive = ['destructive', 'warning'].includes(appearance); const cname = classnames(others.className, { - 'a-btn': isButton || type === 'destructive', - 'a-btn--link': type === 'destructive', - 'a-btn--warning': type === 'destructive', + 'a-btn': isButton || isDestructive, + 'a-btn--link': isDestructive && !isButton, + 'a-btn--warning': isDestructive, + 'a-btn--secondary': isButton && appearance === 'secondary', 'a-link--jump': isJump, 'a-link': shouldUseLinkStyles, });