TRIVIAL: Add xray-core-subnet-mimicry article (#4)
This commit is contained in:
1 parent
49b545f1b4
commit
ea35a3d9f5
21 files changed
+785
-79
No files matched your search
@@ -33,6 +33,18 @@ export const Default: Story = {
|
||||
),
|
||||
};
|
||||
|
||||
export const Interactive: Story = {
|
||||
render: () => (
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.Item href="/">Home</Breadcrumbs.Item>
|
||||
<Breadcrumbs.Separator variant="arrow" />
|
||||
<Breadcrumbs.Item onClick={() => alert('Clicked!')}>Click Me</Breadcrumbs.Item>
|
||||
<Breadcrumbs.Separator variant="arrow" />
|
||||
<Breadcrumbs.Item isCurrent>Current Page</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
),
|
||||
};
|
||||
|
||||
export const WithArrows: Story = {
|
||||
render: () => (
|
||||
<Breadcrumbs>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Breadcrumbs } from './Breadcrumbs';
|
||||
import { render, screen } from '../../lib/testing';
|
||||
import { render, screen, fireEvent } from '../../lib/testing';
|
||||
import { Link } from '../Link/Link';
|
||||
|
||||
describe('components/Breadcrumbs', () => {
|
||||
@@ -12,14 +12,58 @@ describe('components/Breadcrumbs', () => {
|
||||
<Link href="/">Home</Link>
|
||||
</Breadcrumbs.Item>
|
||||
<Breadcrumbs.Separator variant="slash" />
|
||||
<Breadcrumbs.Item>Current</Breadcrumbs.Item>
|
||||
<Breadcrumbs.Item isCurrent>Current</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('navigation')).toHaveAttribute('aria-label', 'Breadcrumb');
|
||||
expect(screen.getByRole('link', { name: 'Home' })).toBeInTheDocument();
|
||||
expect(screen.getByText('/')).toBeInTheDocument();
|
||||
expect(screen.getByText('Current')).toBeInTheDocument();
|
||||
|
||||
const currentItem = screen.getByText('Current').parentElement;
|
||||
expect(currentItem).toHaveAttribute('aria-current', 'page');
|
||||
});
|
||||
|
||||
it('supports isCurrent state', () => {
|
||||
render(
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.Item isCurrent>Current Page</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
);
|
||||
const item = screen.getByText('Current Page').parentElement;
|
||||
expect(item).toHaveAttribute('aria-current', 'page');
|
||||
});
|
||||
|
||||
it('supports href directly on Item', () => {
|
||||
render(
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.Item href="/test-url">Test Link</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
);
|
||||
const link = screen.getByRole('link', { name: 'Test Link' });
|
||||
expect(link).toHaveAttribute('href', '/test-url');
|
||||
});
|
||||
|
||||
it('supports onClick on Item', () => {
|
||||
const handleClick = vi.fn();
|
||||
render(
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.Item onClick={handleClick}>Clickable</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
);
|
||||
const button = screen.getByRole('button', { name: 'Clickable' });
|
||||
fireEvent.click(button);
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('renders as a non-submit button when onClick is provided', () => {
|
||||
render(
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.Item onClick={vi.fn()}>Clickable</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
);
|
||||
const button = screen.getByRole('button', { name: 'Clickable' });
|
||||
expect(button).toHaveAttribute('type', 'button');
|
||||
});
|
||||
|
||||
it('supports different separator variants', () => {
|
||||
|
||||
@@ -29,20 +29,57 @@ const BreadcrumbsRoot = memo(function Breadcrumbs({ children, ...props }: Breadc
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Props for the individual Breadcrumbs item.
|
||||
*/
|
||||
export interface BreadcrumbsItemProps extends TestIdProps {
|
||||
/** Content to be rendered inside the item */
|
||||
children: React.ReactNode;
|
||||
/** Optional className for the list item */
|
||||
className?: string;
|
||||
/** Whether this is the current page */
|
||||
isCurrent?: boolean;
|
||||
/** Optional href for the item (renders as a link) */
|
||||
href?: string;
|
||||
/** Optional click handler for the item */
|
||||
onClick?: React.MouseEventHandler<HTMLElement>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual step in the breadcrumb trail.
|
||||
*/
|
||||
const BreadcrumbsItem = memo(function BreadcrumbsItem({
|
||||
children,
|
||||
className,
|
||||
isCurrent,
|
||||
href,
|
||||
onClick,
|
||||
'data-testid': testId,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
} & TestIdProps) {
|
||||
}: BreadcrumbsItemProps) {
|
||||
const isClickable = !!(href ?? onClick);
|
||||
|
||||
return (
|
||||
<li className={className} data-testid={testId}>
|
||||
<Text color="muted" size="m" type="label">
|
||||
<li
|
||||
aria-current={isCurrent ? 'page' : undefined}
|
||||
className={cn(
|
||||
'group flex items-center transition-all duration-200',
|
||||
!isCurrent && isClickable && 'cursor-pointer',
|
||||
className
|
||||
)}
|
||||
data-testid={testId}
|
||||
>
|
||||
<Text
|
||||
as={href ? 'a' : onClick ? 'button' : 'span'}
|
||||
className={cn(
|
||||
'transition-all duration-200',
|
||||
!isCurrent && 'group-hover:text-[var(--rb-text)] group-hover:underline underline-offset-4'
|
||||
)}
|
||||
color={isCurrent ? 'default' : 'muted'}
|
||||
href={href}
|
||||
size="m"
|
||||
type="label"
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
</li>
|
||||
|
||||
Reference in new issue
Block a user