diff --git a/src/entities/experience/index.ts b/src/entities/experience/index.ts new file mode 100644 index 0000000..eb0fc50 --- /dev/null +++ b/src/entities/experience/index.ts @@ -0,0 +1 @@ +export { ExperienceCard } from './ui/ExperienceCard' diff --git a/src/entities/experience/ui/ExperienceCard.test.tsx b/src/entities/experience/ui/ExperienceCard.test.tsx new file mode 100644 index 0000000..f8257e4 --- /dev/null +++ b/src/entities/experience/ui/ExperienceCard.test.tsx @@ -0,0 +1,72 @@ +import { describe, it, expect } from 'vitest' +import { render, screen } from '@testing-library/react' +import { ExperienceCard } from './ExperienceCard' + +const DEFAULT_PROPS = { + title: 'Senior Developer', + company: 'Acme Corp', + period: '2021 – 2024', + description: 'Built scalable frontend systems.', +} + +describe('ExperienceCard', () => { + describe('rendering', () => { + it('renders the job title', () => { + render() + expect(screen.getByText('Senior Developer')).toBeInTheDocument() + }) + + it('renders the company name', () => { + render() + expect(screen.getByText('Acme Corp')).toBeInTheDocument() + }) + + it('renders the period badge', () => { + render() + expect(screen.getByText('2021 – 2024')).toBeInTheDocument() + }) + + it('renders the description', () => { + render() + expect(screen.getByText('Built scalable frontend systems.')).toBeInTheDocument() + }) + }) + + describe('structure', () => { + it('title is rendered as an h4', () => { + render() + expect(screen.getByRole('heading', { level: 4 })).toHaveTextContent('Senior Developer') + }) + + it('period badge has brutal-border, bg-carbon-black, text-ochre-clay, text-sm', () => { + render() + const badge = screen.getByText('2021 – 2024') + expect(badge).toHaveClass('brutal-border', 'bg-carbon-black', 'text-ochre-clay', 'text-sm') + }) + + it('company paragraph has opacity-80', () => { + render() + const company = screen.getByText('Acme Corp') + expect(company.tagName).toBe('P') + expect(company).toHaveClass('opacity-80') + }) + + it('description paragraph has text-base and max-w-[700px]', () => { + render() + const desc = screen.getByText('Built scalable frontend systems.') + expect(desc).toHaveClass('text-base', 'max-w-[700px]') + }) + + it('card has brutal-border class (from Card component)', () => { + const { container } = render() + expect(container.firstChild).toHaveClass('brutal-border') + }) + }) + + describe('className passthrough', () => { + it('forwards className to the card', () => { + const { container } = render() + expect(container.firstChild).toHaveClass('custom-class') + }) + }) +}) diff --git a/src/entities/experience/ui/ExperienceCard.tsx b/src/entities/experience/ui/ExperienceCard.tsx new file mode 100644 index 0000000..b9e77df --- /dev/null +++ b/src/entities/experience/ui/ExperienceCard.tsx @@ -0,0 +1,44 @@ +import { Card } from '$shared/ui' + +type Props = { + /** + * Job title + */ + title: string + /** + * Company name + */ + company: string + /** + * Employment period (e.g. "2021 – 2024") + */ + period: string + /** + * Description of responsibilities and achievements + */ + description: string + /** + * Additional CSS classes forwarded to the card + */ + className?: string +} + +/** + * Work experience card with title, company, period, and description. + */ +export function ExperienceCard({ title, company, period, description, className }: Props) { + return ( + +
+
+

{title}

+

{company}

+
+ + {period} + +
+

{description}

+
+ ) +} diff --git a/src/entities/index.ts b/src/entities/index.ts new file mode 100644 index 0000000..46d2f42 --- /dev/null +++ b/src/entities/index.ts @@ -0,0 +1,2 @@ +export * from './project' +export * from './experience'