T7: Error Boundaries
T7: Error Boundaries
Section titled “T7: Error Boundaries”Phase: BCG Phase 1 Depends on: T6 (ResultsPanel декомпозирован) Effort: ~1.5h
Context
Section titled “Context”Сейчас любой throw в chart-компоненте (Recharts, ForestPlot) роняет весь React subtree. Нужны два error boundary:
ErrorBoundary— generic, вокруг крупных блоков layoutChartErrorBoundary— специфичный, показывает “Chart unavailable” + raw data
Читать:
archive/2026-04-23-bcg-planning-docs/BCG_plan.md§1.4.1-1.4.3src/App.tsx,src/components/results/*(PowerCurveSection, ForestPlot)
Добавить два error boundary, обернуть layout и chart-компоненты, покрыть тестами.
1. src/components/ErrorBoundary.tsx
Section titled “1. src/components/ErrorBoundary.tsx”Class component:
interface Props { fallback?: ReactNode; onError?: (error: Error, errorInfo: ErrorInfo) => void; children: ReactNode;}
interface State { hasError: boolean; error: Error | null;}
export class ErrorBoundary extends Component<Props, State> { state = { hasError: false, error: null }; static getDerivedStateFromError(error: Error): State { return { hasError: true, error }; } componentDidCatch(error: Error, info: ErrorInfo) { this.props.onError?.(error, info); // optional: console.error(error, info) } render() { if (this.state.hasError) { return this.props.fallback ?? ( <div className="error-boundary-fallback"> <h3>Something went wrong</h3> <button onClick={() => this.setState({ hasError: false, error: null })}> Retry </button> </div> ); } return this.props.children; }}Стили в src/styles/components.css (или inline module).
2. src/components/ChartErrorBoundary.tsx
Section titled “2. src/components/ChartErrorBoundary.tsx”Обёртка над ErrorBoundary с специфичным fallback:
<ErrorBoundary fallback={ <div className="chart-error"> <p>Chart unavailable</p> {rawData ? <pre>{JSON.stringify(rawData, null, 2)}</pre> : null} </div>}> {children}</ErrorBoundary>Prop rawData?: unknown для показа данных, если чарт не смог отрендериться.
3. Обернуть в App.tsx
Section titled “3. Обернуть в App.tsx”<ErrorBoundary><WizardPanel /></ErrorBoundary><ErrorBoundary><SidebarPanel /></ErrorBoundary>Crash в sidebar не должен ронять wizard, и наоборот.
4. Обернуть chart-компоненты
Section titled “4. Обернуть chart-компоненты”В src/components/results/:
PowerCurveSection→<ChartErrorBoundary rawData={powerCurveData}>...</ChartErrorBoundary>ObservedResultsSection(ForestPlot) → то же самоеSensitivitySection(если внутри есть chart)
5. Тесты
Section titled “5. Тесты”src/components/ErrorBoundary.test.tsx:
- Компонент без ошибок → рендерит children
- Компонент throws → рендерит fallback
- Retry button сбрасывает state
onErrorcallback вызывается
src/components/ChartErrorBoundary.test.tsx:
- Throws внутри → “Chart unavailable” + pre с rawData
Тестовый компонент-броскун:
function BrokenComponent(): never { throw new Error("boom"); }6. Verify
Section titled “6. Verify”cd app/frontendnpx vitest run src/components/ErrorBoundary.test.tsx src/components/ChartErrorBoundary.test.tsxnpx vitest runnpx tsc --noEmitnpm run buildРучная проверка: временно бросить ошибку в PowerCurveSection (throw new Error("test")) → только chart показывает fallback, остальные секции живые, layout не сломан. Откатить.
Done When
Section titled “Done When”-
ErrorBoundary.tsx+ тесты -
ChartErrorBoundary.tsx+ тесты -
App.tsxоборачивает WizardPanel и SidebarPanel - Chart-компоненты обёрнуты в
ChartErrorBoundary - Все тесты зелёные
Constraints
Section titled “Constraints”- Fallback UI — минимальный, без дизайнерских изысков (это уровень Phase 2)
- Не логировать в production —
console.errorтолько, никакой телеметрии (Sentry/PostHog — это Phase 3)