Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions apps/www/src/content/docs/components/link/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ Additional style variations including underlined and external links.

<Demo data={styleDemo} />

### Custom render

Use the `render` prop to render as a custom element such as a router `Link`. All props (including `external`, `download`, and ARIA attributes) are forwarded to the provided element.

```tsx
import { Link as RouterLink } from "react-router-dom";

<Link render={<RouterLink to="/about" />}>About</Link>
```

## Accessibility

The Link component follows accessibility best practices:
Expand Down
16 changes: 14 additions & 2 deletions apps/www/src/content/docs/components/link/props.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import type { ReactElement } from 'react';

export interface LinkProps {
/** The URL that the link points to. (Required) */
href: string;
/** The URL that the link points to. */
href?: string;

/** Whether the link should open in a new tab. */
external?: boolean;

/** Whether the link should be downloadable or a string for the filename. */
download?: boolean | string;

/**
* Custom element used to render the Link.
*
* All props are forwarded to the specified element. Useful for integrating
* with router libraries (e.g. `<NextLink />`, `<RouterLink />`).
*
* @default "<a />"
*/
render?: ReactElement;
Comment thread
rohanchkrabrty marked this conversation as resolved.

/** Additional CSS class names. */
className?: string;
}
41 changes: 41 additions & 0 deletions packages/raystack/components/link/__tests__/link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,47 @@ describe('Link', () => {
});
});

describe('Custom render', () => {
it('renders as the element provided via render prop', () => {
render(
<Link href='/test' render={<button type='button' />}>
Custom
</Link>
);
const link = screen.getByRole('link');
expect(link.tagName).toBe('BUTTON');
expect(link).toHaveAttribute('type', 'button');
});

it('forwards Link props to the custom rendered element', () => {
render(
<Link
href='https://example.com'
external
render={<a data-custom='yes' />}
>
External
</Link>
);
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', 'https://example.com');
expect(link).toHaveAttribute('target', '_blank');
expect(link).toHaveAttribute('rel', 'noopener noreferrer');
expect(link).toHaveAttribute('data-custom', 'yes');
});

it('preserves the link className on the custom rendered element', () => {
render(
<Link href='/test' render={<div />}>
Custom
</Link>
);
const link = screen.getByRole('link');
expect(link.tagName).toBe('DIV');
expect(link).toHaveClass(styles.link);
});
});

describe('HTML Attributes', () => {
it('supports title attribute', () => {
render(
Expand Down
10 changes: 6 additions & 4 deletions packages/raystack/components/link/link.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useRender } from '@base-ui/react';
import { cx } from 'class-variance-authority';
import { ComponentProps } from 'react';
import { Text, type TextBaseProps } from '../text';
import styles from './link.module.css';

export interface LinkProps extends TextBaseProps, ComponentProps<'a'> {
href: string;
export interface LinkProps
extends TextBaseProps,
useRender.ComponentProps<'a'> {
external?: boolean;
download?: boolean | string;
}
Expand All @@ -16,6 +17,7 @@ export function Link({
size = 'small',
external,
download,
render = <a />,
...props
}: LinkProps) {
const externalProps = external
Expand All @@ -42,7 +44,7 @@ export function Link({
{...externalProps}
{...downloadProps}
{...props}
render={<a />}
render={render}
>
{children}
</Text>
Expand Down
Loading