diff --git a/.changeset/link-underline-prop.md b/.changeset/link-underline-prop.md new file mode 100644 index 000000000..ff2072e65 --- /dev/null +++ b/.changeset/link-underline-prop.md @@ -0,0 +1,5 @@ +--- +"@launchpad-ui/components": minor +--- + +Add `underline` prop to Link component with `'always' | 'hover' | 'none'` values diff --git a/packages/components/__tests__/Link.spec.tsx b/packages/components/__tests__/Link.spec.tsx index a5f47e8ee..f3f18a3f3 100644 --- a/packages/components/__tests__/Link.spec.tsx +++ b/packages/components/__tests__/Link.spec.tsx @@ -30,4 +30,18 @@ describe('Link', () => { expect(screen.getByRole('link')).toBeVisible(); expect(screen.getByRole('link')).toHaveAttribute('href', 'https://www.test.com'); }); + + it('renders with underline="always"', () => { + const navigate = vi.fn(); + render( + + + + Link + + + , + ); + expect(screen.getByRole('link')).toBeVisible(); + }); }); diff --git a/packages/components/src/Link.tsx b/packages/components/src/Link.tsx index 481b5c37e..7e775c9c2 100644 --- a/packages/components/src/Link.tsx +++ b/packages/components/src/Link.tsx @@ -16,14 +16,22 @@ const linkStyles = cva(styles.base, { default: styles.default, subtle: styles.subtle, }, + underline: { + always: styles.underlineAlways, + hover: styles.underlineHover, + none: styles.underlineNone, + }, }, defaultVariants: { variant: 'default', + underline: 'hover', }, }); interface LinkProps extends AriaLinkProps, VariantProps, DOMProps { ref?: Ref; + /** Controls when the link is underlined. */ + underline?: 'always' | 'hover' | 'none'; } const LinkContext = createContext>(null); @@ -35,14 +43,14 @@ const LinkContext = createContext>(nu */ const Link = ({ ref, ...props }: LinkProps) => { [props, ref] = useLPContextProps(props, ref, LinkContext); - const { variant = 'default' } = props; + const { variant = 'default', underline = 'hover' } = props; return ( - linkStyles({ ...renderProps, variant, className }), + linkStyles({ ...renderProps, variant, underline, className }), )} /> ); diff --git a/packages/components/src/styles/Link.module.css b/packages/components/src/styles/Link.module.css index 1f7bcc9b3..fe94995df 100644 --- a/packages/components/src/styles/Link.module.css +++ b/packages/components/src/styles/Link.module.css @@ -41,3 +41,18 @@ composes: link; color: var(--lp-color-text-ui-secondary); } + +.underlineAlways { + text-decoration: underline; +} + +.underlineHover { +} + +.underlineNone { + text-decoration: none; + + &[data-hovered] { + text-decoration: none; + } +} diff --git a/packages/components/stories/Link.stories.tsx b/packages/components/stories/Link.stories.tsx index 4d827bfb7..166cbdcc4 100644 --- a/packages/components/stories/Link.stories.tsx +++ b/packages/components/stories/Link.stories.tsx @@ -35,6 +35,22 @@ export const Subtle: Story = { }, }; +export const UnderlineAlways: Story = { + args: { + children: 'Link', + href: '/test', + underline: 'always', + }, +}; + +export const UnderlineNone: Story = { + args: { + children: 'Link', + href: '/test', + underline: 'none', + }, +}; + export const States: Story = { render: (args) => { const styles = { width: 'fit-content' };