# Spec 37.1: Foundation Components **Status**: READY FOR IMPLEMENTATION **Phase**: 1 of 5 **Depends On**: Spec 37.0 (Project Setup) **Goal**: TDD implementation of Layout and MetricsPanel components --- ## Deliverables By the end of this phase, you will have: 1. `Layout` component with header and main content area 2. `MetricsPanel` component displaying segmentation results 3. 100% test coverage for both components 4. Visual verification in browser --- ## Component 1: Layout ### Test First Create `src/components/__tests__/Layout.test.tsx`: ```typescript import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { Layout } from '../Layout' describe('Layout', () => { it('renders header with title', () => { render(Content) expect( screen.getByRole('heading', { name: /stroke lesion segmentation/i }) ).toBeInTheDocument() }) it('renders subtitle', () => { render(Content) expect(screen.getByText(/deepisles segmentation/i)).toBeInTheDocument() }) it('renders children in main area', () => { render(
Test Child
) expect(screen.getByTestId('child')).toBeInTheDocument() }) it('has accessible landmark structure', () => { render(Content) expect(screen.getByRole('banner')).toBeInTheDocument() expect(screen.getByRole('main')).toBeInTheDocument() }) it('applies dark theme styling', () => { render(Content) const container = screen.getByRole('banner').parentElement expect(container).toHaveClass('bg-gray-950') }) }) ``` ### Implementation Create `src/components/Layout.tsx`: ```typescript import { ReactNode } from 'react' interface LayoutProps { children: ReactNode } export function Layout({ children }: LayoutProps) { return (

Stroke Lesion Segmentation

DeepISLES segmentation on ISLES24 dataset

{children}
) } ``` ### Verify ```bash npm test -- Layout # Expected: 5 tests passing ``` --- ## Component 2: MetricsPanel ### Test First Create `src/components/__tests__/MetricsPanel.test.tsx`: ```typescript import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { MetricsPanel } from '../MetricsPanel' describe('MetricsPanel', () => { const defaultMetrics = { caseId: 'sub-stroke0001', diceScore: 0.847, volumeMl: 15.32, elapsedSeconds: 12.5, } it('renders results heading', () => { render() expect( screen.getByRole('heading', { name: /results/i }) ).toBeInTheDocument() }) it('displays case ID', () => { render() expect(screen.getByText('sub-stroke0001')).toBeInTheDocument() }) it('displays dice score with 3 decimal places', () => { render() expect(screen.getByText('0.847')).toBeInTheDocument() }) it('displays volume in mL with 2 decimal places', () => { render() expect(screen.getByText('15.32 mL')).toBeInTheDocument() }) it('displays elapsed time with 1 decimal place', () => { render() expect(screen.getByText('12.5s')).toBeInTheDocument() }) it('hides dice score row when null', () => { render( ) expect(screen.queryByText(/dice score/i)).not.toBeInTheDocument() }) it('hides volume row when null', () => { render( ) expect(screen.queryByText(/volume/i)).not.toBeInTheDocument() }) it('applies card styling', () => { render() const panel = screen.getByRole('heading', { name: /results/i }).parentElement expect(panel).toHaveClass('bg-gray-800', 'rounded-lg') }) }) ``` ### Implementation Create `src/components/MetricsPanel.tsx`: ```typescript interface Metrics { caseId: string diceScore: number | null volumeMl: number | null elapsedSeconds: number } interface MetricsPanelProps { metrics: Metrics } export function MetricsPanel({ metrics }: MetricsPanelProps) { return (

Results

Case: {metrics.caseId}
{metrics.diceScore !== null && (
Dice Score: {metrics.diceScore.toFixed(3)}
)} {metrics.volumeMl !== null && (
Volume: {metrics.volumeMl.toFixed(2)} mL
)}
Time: {metrics.elapsedSeconds.toFixed(1)}s
) } ``` ### Verify ```bash npm test -- MetricsPanel # Expected: 8 tests passing ``` --- ## Create Index Export Create `src/components/index.ts`: ```typescript export { Layout } from './Layout' export { MetricsPanel } from './MetricsPanel' ``` --- ## Visual Verification Update `src/App.tsx` to see components: ```typescript import { Layout } from './components/Layout' import { MetricsPanel } from './components/MetricsPanel' const mockMetrics = { caseId: 'sub-stroke0001', diceScore: 0.847, volumeMl: 15.32, elapsedSeconds: 12.5, } function App() { return (
) } export default App ``` Run dev server and verify visually: ```bash npm run dev # Open http://localhost:5173 ``` --- ## Verification Checklist - [ ] `npm test` - All 13+ tests pass - [ ] `npm run dev` - Components render correctly - [ ] Header shows "Stroke Lesion Segmentation" - [ ] MetricsPanel shows all metrics with correct formatting - [ ] Dark theme applies correctly --- ## File Structure After This Phase ``` frontend/src/ ├── components/ │ ├── __tests__/ │ │ ├── Layout.test.tsx │ │ └── MetricsPanel.test.tsx │ ├── Layout.tsx │ ├── MetricsPanel.tsx │ └── index.ts ├── mocks/ ├── test/ ├── App.tsx (updated) └── ... ``` --- ## Next Phase Once verification passes, proceed to **Spec 37.2: API Layer**