chore: format codebase and move SectionAccordion to entities/Section
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||
import { ExperienceCard } from './ExperienceCard'
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
||||
import { ExperienceCard } from './ExperienceCard';
|
||||
|
||||
const meta: Meta<typeof ExperienceCard> = {
|
||||
title: 'Entities/ExperienceCard',
|
||||
@@ -11,30 +11,28 @@ const meta: Meta<typeof ExperienceCard> = {
|
||||
</div>
|
||||
),
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
export default meta
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof ExperienceCard>
|
||||
type Story = StoryObj<typeof ExperienceCard>;
|
||||
|
||||
const baseArgs = {
|
||||
title: 'Senior Frontend Engineer',
|
||||
company: 'Acme Corp',
|
||||
period: '2021 – 2024',
|
||||
description: 'Led frontend development for the core product, established design system practices, and mentored junior engineers across two distributed teams.',
|
||||
}
|
||||
description:
|
||||
'Led frontend development for the core product, established design system practices, and mentored junior engineers across two distributed teams.',
|
||||
};
|
||||
|
||||
export const Default: Story = {
|
||||
args: baseArgs,
|
||||
}
|
||||
};
|
||||
|
||||
export const SlateBackground: Story = {
|
||||
render: () => (
|
||||
<div className="bg-slate-indigo p-8 max-w-2xl">
|
||||
<ExperienceCard
|
||||
{...baseArgs}
|
||||
className="border-ochre-clay"
|
||||
/>
|
||||
<ExperienceCard {...baseArgs} className="border-ochre-clay" />
|
||||
</div>
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { ExperienceCard } from './ExperienceCard'
|
||||
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(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
expect(screen.getByText('Senior Developer')).toBeInTheDocument()
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
expect(screen.getByText('Senior Developer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the company name', () => {
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
expect(screen.getByText('Acme Corp')).toBeInTheDocument()
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
expect(screen.getByText('Acme Corp')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the period badge', () => {
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
expect(screen.getByText('2021 – 2024')).toBeInTheDocument()
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
expect(screen.getByText('2021 – 2024')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the description', () => {
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
expect(screen.getByText('Built scalable frontend systems.')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
expect(screen.getByText('Built scalable frontend systems.')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('structure', () => {
|
||||
it('title is rendered as an h4', () => {
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
expect(screen.getByRole('heading', { level: 4 })).toHaveTextContent('Senior Developer')
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
expect(screen.getByRole('heading', { level: 4 })).toHaveTextContent('Senior Developer');
|
||||
});
|
||||
|
||||
it('period badge has brutal-border, bg-carbon-black, text-ochre-clay, text-sm', () => {
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
const badge = screen.getByText('2021 – 2024')
|
||||
expect(badge).toHaveClass('brutal-border', 'bg-carbon-black', 'text-ochre-clay', 'text-sm')
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
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(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
const company = screen.getByText('Acme Corp')
|
||||
expect(company.tagName).toBe('P')
|
||||
expect(company).toHaveClass('opacity-80')
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
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(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
const desc = screen.getByText('Built scalable frontend systems.')
|
||||
expect(desc).toHaveClass('text-base', 'max-w-[700px]')
|
||||
})
|
||||
render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
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(<ExperienceCard {...DEFAULT_PROPS} />)
|
||||
expect(container.firstChild).toHaveClass('brutal-border')
|
||||
})
|
||||
})
|
||||
const { container } = render(<ExperienceCard {...DEFAULT_PROPS} />);
|
||||
expect(container.firstChild).toHaveClass('brutal-border');
|
||||
});
|
||||
});
|
||||
|
||||
describe('className passthrough', () => {
|
||||
it('forwards className to the card', () => {
|
||||
const { container } = render(<ExperienceCard {...DEFAULT_PROPS} className="custom-class" />)
|
||||
expect(container.firstChild).toHaveClass('custom-class')
|
||||
})
|
||||
})
|
||||
})
|
||||
const { container } = render(<ExperienceCard {...DEFAULT_PROPS} className="custom-class" />);
|
||||
expect(container.firstChild).toHaveClass('custom-class');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import { Card } from '$shared/ui'
|
||||
import { Card } from '$shared/ui';
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* Job title
|
||||
*/
|
||||
title: string
|
||||
title: string;
|
||||
/**
|
||||
* Company name
|
||||
*/
|
||||
company: string
|
||||
company: string;
|
||||
/**
|
||||
* Employment period (e.g. "2021 – 2024")
|
||||
*/
|
||||
period: string
|
||||
period: string;
|
||||
/**
|
||||
* Description of responsibilities and achievements
|
||||
*/
|
||||
description: string
|
||||
description: string;
|
||||
/**
|
||||
* Additional CSS classes forwarded to the card
|
||||
*/
|
||||
className?: string
|
||||
}
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Work experience card with title, company, period, and description.
|
||||
@@ -34,11 +34,9 @@ export function ExperienceCard({ title, company, period, description, className
|
||||
<h4>{title}</h4>
|
||||
<p className="text-base opacity-80">{company}</p>
|
||||
</div>
|
||||
<span className="brutal-border px-4 py-2 bg-carbon-black text-ochre-clay text-sm self-start">
|
||||
{period}
|
||||
</span>
|
||||
<span className="brutal-border px-4 py-2 bg-carbon-black text-ochre-clay text-sm self-start">{period}</span>
|
||||
</div>
|
||||
<p className="text-base max-w-[700px]">{description}</p>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user