refactor: Refactor UI structure

This commit is contained in:
deytenit committed 2026-02-01 01:49:02 +03:00
1 parent bbb9503172
commit ca20310460
33 files changed
+435 -247

No files matched your search

@@ -0,0 +1,66 @@
import React from 'react';
import { Breadcrumbs } from './Breadcrumbs';
import { Link } from '../Link/Link';
import type { Meta, StoryObj } from '@storybook/react';
const meta: Meta<typeof Breadcrumbs> = {
title: 'components/Breadcrumbs',
component: Breadcrumbs,
tags: ['autodocs'],
};
export default meta;
/**
*
*/
type Story = StoryObj<typeof Breadcrumbs>;
export const Default: Story = {
render: () => (
<Breadcrumbs>
<Breadcrumbs.Item>
<Link href="/">Home</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator variant="slash" />
<Breadcrumbs.Item>
<Link href="/blog">Blog</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator variant="slash" />
<Breadcrumbs.Item>Modern Web Design</Breadcrumbs.Item>
</Breadcrumbs>
),
};
export const WithArrows: Story = {
render: () => (
<Breadcrumbs>
<Breadcrumbs.Item>
<Link href="/">Home</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator variant="arrow" />
<Breadcrumbs.Item>
<Link href="/docs">Docs</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator variant="arrow" />
<Breadcrumbs.Item>Components</Breadcrumbs.Item>
</Breadcrumbs>
),
};
export const WithBullets: Story = {
render: () => (
<Breadcrumbs>
<Breadcrumbs.Item>
<Link href="/">Home</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator variant="bull" />
<Breadcrumbs.Item>
<Link href="/about">About</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator variant="bull" />
<Breadcrumbs.Item>Team</Breadcrumbs.Item>
</Breadcrumbs>
),
};
@@ -0,0 +1,40 @@
import React from 'react';
import { Breadcrumbs } from './Breadcrumbs';
import { render, screen } from '../../../test-utils';
import { Link } from '../Link/Link';
describe('components/generic/Breadcrumbs', () => {
it('renders correctly with multiple items and separators', () => {
render(
<Breadcrumbs>
<Breadcrumbs.Item>
<Link href="/">Home</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator variant="slash" />
<Breadcrumbs.Item>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();
});
it('supports different separator variants', () => {
const { rerender } = render(
<Breadcrumbs>
<Breadcrumbs.Separator data-testid="sep" variant="bull" />
</Breadcrumbs>
);
expect(screen.getByTestId('sep')).toHaveTextContent('•');
rerender(
<Breadcrumbs>
<Breadcrumbs.Separator data-testid="sep" variant="slash" />
</Breadcrumbs>
);
expect(screen.getByTestId('sep')).toHaveTextContent('/');
});
});
@@ -0,0 +1,102 @@
import { cn } from '@ermnvldmr/stl';
import { ChevronRight } from 'lucide-react';
import React, { memo } from 'react';
import { HStack } from '../HStack/HStack';
import { Text } from '../Text/Text';
import type { HStackProps } from '../HStack/HStack';
import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
/**
* Props for the Breadcrumbs component.
*/
export interface BreadcrumbsProps extends HStackProps {
/** Breadcrumb items and separators */
children: React.ReactNode;
}
/**
* Root component for hierarchical navigation.
*/
const BreadcrumbsRoot = memo(function Breadcrumbs({ children, ...props }: BreadcrumbsProps) {
return (
<nav aria-label="Breadcrumb">
<HStack align="center" as="ol" gap={2} {...props}>
{children}
</HStack>
</nav>
);
});
/**
* Individual step in the breadcrumb trail.
*/
const BreadcrumbsItem = memo(function BreadcrumbsItem({
children,
className,
'data-testid': testId,
}: {
children: React.ReactNode;
className?: string;
} & TestIdProps) {
return (
<li className={className} data-testid={testId}>
<Text color="muted" size="m" type="label">
{children}
</Text>
</li>
);
});
/**
* Visual divider between breadcrumb items.
*/
export interface BreadcrumbsSeparatorProps extends ClassNameProps, TestIdProps {
/** Visual variant of the separator */
variant?: 'slash' | 'bull' | 'arrow';
}
const BreadcrumbsSeparator = memo(function BreadcrumbsSeparator({
variant = 'slash',
className,
'data-testid': testId,
}: BreadcrumbsSeparatorProps) {
const content = {
slash: '/',
bull: '•',
arrow: <ChevronRight className="size-4" />,
};
return (
<li
aria-hidden="true"
className={cn('text-[var(--rb-muted-text)]', className)}
data-testid={testId}
>
<Text color="muted" size="m" type="label">
{content[variant]}
</Text>
</li>
);
});
/**
* A hierarchical navigation component.
*
* Breadcrumbs provides a flexible way to display the current location within
* a site's hierarchy. It uses a compound component pattern for maximum flexibility.
*
* @example
* ```tsx
* <Breadcrumbs>
* <Breadcrumbs.Item><Link href="/">Home</Link></Breadcrumbs.Item>
* <Breadcrumbs.Separator variant="arrow" />
* <Breadcrumbs.Item>Current Page</Breadcrumbs.Item>
* </Breadcrumbs>
* ```
*/
export const Breadcrumbs = Object.assign(BreadcrumbsRoot, {
Item: BreadcrumbsItem,
Separator: BreadcrumbsSeparator,
});