refactor: widen section and sidebar, plain period text, Badge xs size for stack
This commit is contained in:
@@ -64,10 +64,11 @@ describe('ExperienceCard', () => {
|
|||||||
expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent('Senior Developer');
|
expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent('Senior Developer');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('period badge has brutal-border, bg-blue, text-cream, md size', () => {
|
it('period has left border accent styling', () => {
|
||||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||||
const badge = screen.getByText('2021 – 2024');
|
const period = screen.getByText('2021 – 2024');
|
||||||
expect(badge).toHaveClass('brutal-border', 'bg-blue', 'text-cream', 'px-4', 'py-2', 'text-sm');
|
expect(period.tagName).toBe('P');
|
||||||
|
expect(period).toHaveClass('brutal-border-left', 'text-sm');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('description renders via RichText with rich-text class', () => {
|
it('description renders via RichText with rich-text class', () => {
|
||||||
@@ -83,12 +84,13 @@ describe('ExperienceCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('stack tags', () => {
|
describe('stack tags', () => {
|
||||||
it('renders stack tags in the sidebar', () => {
|
it('renders stack tags in the sidebar as xs outline badges', () => {
|
||||||
render(<ExperienceCard {...DEFAULT_PROPS} stack={['React', 'TypeScript']} />);
|
render(<ExperienceCard {...DEFAULT_PROPS} stack={['React', 'TypeScript']} />);
|
||||||
const react = screen.getByText('React');
|
const react = screen.getByText('React');
|
||||||
const ts = screen.getByText('TypeScript');
|
const ts = screen.getByText('TypeScript');
|
||||||
expect(react.closest('.brutal-border-sidebar')).toBeInTheDocument();
|
expect(react.closest('.brutal-border-sidebar')).toBeInTheDocument();
|
||||||
expect(ts.closest('.brutal-border-sidebar')).toBeInTheDocument();
|
expect(ts.closest('.brutal-border-sidebar')).toBeInTheDocument();
|
||||||
|
expect(react).toHaveClass('brutal-border', 'bg-transparent', 'px-2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders nothing extra when stack is empty', () => {
|
it('renders nothing extra when stack is empty', () => {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type Props = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Work experience card with sidebar layout.
|
* Work experience card with sidebar layout.
|
||||||
* Sidebar: period badge, company, stack tags.
|
* Sidebar: period, company, stack tags.
|
||||||
* Main: job title and rich-text description.
|
* Main: job title and rich-text description.
|
||||||
*/
|
*/
|
||||||
export function ExperienceCard({ title, company, period, description, stack, className }: Props) {
|
export function ExperienceCard({ title, company, period, description, stack, className }: Props) {
|
||||||
@@ -38,12 +38,12 @@ export function ExperienceCard({ title, company, period, description, stack, cla
|
|||||||
<CardSidebar
|
<CardSidebar
|
||||||
sidebar={
|
sidebar={
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<Badge size="md">{period}</Badge>
|
<p className="text-sm font-medium brutal-border-left pl-3">{period}</p>
|
||||||
<p className="text-base font-medium">{company}</p>
|
<p className="text-base font-medium">{company}</p>
|
||||||
{stack.length > 0 && (
|
{stack.length > 0 && (
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{stack.map((tech) => (
|
{stack.map((tech) => (
|
||||||
<Badge key={tech} variant="outline">
|
<Badge key={tech} variant="outline" size="xs">
|
||||||
{tech}
|
{tech}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
/* === GRID === */
|
/* === GRID === */
|
||||||
--grid-gap: var(--space-3);
|
--grid-gap: var(--space-3);
|
||||||
--section-content-width: 56rem;
|
--section-content-width: 72rem;
|
||||||
|
|
||||||
/* === ANIMATION === */
|
/* === ANIMATION === */
|
||||||
--ease-default: ease;
|
--ease-default: ease;
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ describe('Badge', () => {
|
|||||||
expect(screen.getByText('Tag')).toHaveClass('px-3', 'py-1', 'text-xs');
|
expect(screen.getByText('Tag')).toHaveClass('px-3', 'py-1', 'text-xs');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('applies xs size classes', () => {
|
||||||
|
render(<Badge size="xs">Tag</Badge>);
|
||||||
|
expect(screen.getByText('Tag')).toHaveClass('px-2', 'py-0.5');
|
||||||
|
});
|
||||||
|
|
||||||
it('applies sm size classes', () => {
|
it('applies sm size classes', () => {
|
||||||
render(<Badge size="sm">Tag</Badge>);
|
render(<Badge size="sm">Tag</Badge>);
|
||||||
expect(screen.getByText('Tag')).toHaveClass('px-3', 'py-1', 'text-xs');
|
expect(screen.getByText('Tag')).toHaveClass('px-3', 'py-1', 'text-xs');
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
|
|||||||
import { cn } from '$shared/lib';
|
import { cn } from '$shared/lib';
|
||||||
|
|
||||||
export type BadgeVariant = 'default' | 'primary' | 'secondary' | 'outline';
|
export type BadgeVariant = 'default' | 'primary' | 'secondary' | 'outline';
|
||||||
export type BadgeSize = 'sm' | 'md';
|
export type BadgeSize = 'xs' | 'sm' | 'md';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +33,7 @@ const VARIANTS: Record<BadgeVariant, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SIZES: Record<BadgeSize, string> = {
|
const SIZES: Record<BadgeSize, string> = {
|
||||||
|
xs: 'px-2 py-0.5 text-[10px]',
|
||||||
sm: 'px-3 py-1 text-xs',
|
sm: 'px-3 py-1 text-xs',
|
||||||
md: 'px-4 py-2 text-sm',
|
md: 'px-4 py-2 text-sm',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ describe('CardSidebar', () => {
|
|||||||
it('sidebar column has fixed width on md', () => {
|
it('sidebar column has fixed width on md', () => {
|
||||||
render(<CardSidebar sidebar={<span>Sidebar</span>}>Main</CardSidebar>);
|
render(<CardSidebar sidebar={<span>Sidebar</span>}>Main</CardSidebar>);
|
||||||
const sidebar = screen.getByText('Sidebar').parentElement;
|
const sidebar = screen.getByText('Sidebar').parentElement;
|
||||||
expect(sidebar).toHaveClass('md:w-52');
|
expect(sidebar).toHaveClass('md:w-64');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('main column fills remaining space', () => {
|
it('main column fills remaining space', () => {
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ interface CardSidebarProps {
|
|||||||
export function CardSidebar({ sidebar, children, className }: CardSidebarProps) {
|
export function CardSidebar({ sidebar, children, className }: CardSidebarProps) {
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex flex-col md:flex-row', className)}>
|
<div className={cn('flex flex-col md:flex-row', className)}>
|
||||||
<div className="shrink-0 md:w-52 brutal-border-sidebar pb-6 md:pb-0 md:pr-8 mb-6 md:mb-0">{sidebar}</div>
|
<div className="shrink-0 md:w-64 brutal-border-sidebar pb-6 md:pb-0 md:pr-8 mb-6 md:mb-0">{sidebar}</div>
|
||||||
<div className="flex-1 min-w-0 md:pl-8">{children}</div>
|
<div className="flex-1 min-w-0 md:pl-8">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user