veza/apps/web/src/components/ui/ERROR_DISPLAY_COMPONENT_API.md

303 lines
6.2 KiB
Markdown
Raw Normal View History

# ErrorDisplay Component API Design
**Date**: 2025-01-27
**Action**: 3.1.1.1 - Design ErrorDisplay component API
**Status**: ✅ Complete
## Overview
This document defines the API for a reusable `ErrorDisplay` component that standardizes error presentation across the application, replacing inconsistent toast notifications and inline error divs.
## Component Purpose
The `ErrorDisplay` component provides:
- **Consistent error UI** across the application
- **Contextual error information** (error type, message, details)
- **Recovery actions** (retry, dismiss, show details)
- **Accessibility** (ARIA labels, keyboard navigation)
- **Flexible placement** (inline, modal, banner)
## Component API
### Props Interface
```typescript
interface ErrorDisplayProps {
/**
* The error to display
* Can be Error object, ApiError, string, or error-like object
*/
error: Error | ApiError | string | {
message: string;
code?: string | number;
status?: number;
details?: Record<string, any>;
};
/**
* Callback when user clicks retry button
* If not provided, retry button is hidden
*/
onRetry?: () => void | Promise<void>;
/**
* Callback when user dismisses the error
* If not provided, dismiss button is hidden
*/
onDismiss?: () => void;
/**
* Whether to show detailed error information
* Default: false (only shows user-friendly message)
* In development, defaults to true
*/
showDetails?: boolean;
/**
* Additional context about where/why the error occurred
* Used for logging and debugging
*/
context?: {
action?: string; // e.g., "fetching tracks", "uploading file"
resource?: string; // e.g., "tracks", "playlist"
resourceId?: string; // e.g., track ID, playlist ID
[key: string]: any; // Additional context
};
/**
* Variant/style of error display
* - "inline": Inline error within content (default)
* - "banner": Full-width banner at top of page
* - "modal": Modal dialog overlay
* - "card": Card-style error display
*/
variant?: 'inline' | 'banner' | 'modal' | 'card';
/**
* Error severity/category
* Affects visual styling (color, icon)
* - "error": Critical errors (default)
* - "warning": Warnings that don't block functionality
* - "info": Informational messages
*/
severity?: 'error' | 'warning' | 'info';
/**
* Size of the error display
* - "sm": Small, compact
* - "md": Medium (default)
* - "lg": Large, prominent
*/
size?: 'sm' | 'md' | 'lg';
/**
* Custom className for styling
*/
className?: string;
/**
* Whether error is dismissible
* Default: true (if onDismiss provided)
*/
dismissible?: boolean;
/**
* Custom title override
* If not provided, uses default title based on error type
*/
title?: string;
/**
* Custom icon override
* If not provided, uses default icon based on severity
*/
icon?: React.ReactNode;
/**
* Additional actions to display alongside retry/dismiss
* Array of { label, onClick, variant } objects
*/
actions?: Array<{
label: string;
onClick: () => void;
variant?: 'default' | 'outline' | 'ghost';
}>;
}
```
## Error Type Handling
### Error Object Normalization
The component normalizes different error types:
1. **Error Object**:
```typescript
new Error('Failed to fetch tracks')
```
- Message: `error.message`
- Stack: Available in details (dev mode)
2. **ApiError** (from generated types):
```typescript
{ code: 404, message: 'Track not found', ... }
```
- Message: `error.message`
- Code: `error.code`
- Status: `error.status`
3. **String**:
```typescript
'Network error occurred'
```
- Message: The string itself
4. **Axios Error**:
```typescript
axiosError.response.data.error
```
- Extracts error message from response
- Includes HTTP status code
## Default Behavior
### Error Message Extraction
1. **Priority order**:
- `error.message` (if Error object)
- `error.error?.message` (if ApiError)
- `error.toString()` (fallback)
- `String(error)` (final fallback)
2. **User-friendly messages**:
- Maps technical error codes to user-friendly messages
- Example: `ERR_NETWORK` → "Unable to connect to server. Please check your internet connection."
### Visual Styling
- **Error severity**: Red/error colors
- **Warning severity**: Yellow/warning colors
- **Info severity**: Blue/info colors
### Icons
- **Error**: `AlertTriangle` (red)
- **Warning**: `AlertCircle` (yellow)
- **Info**: `Info` (blue)
## Usage Examples
### Basic Inline Error
```typescript
<ErrorDisplay
error={error}
onRetry={() => refetch()}
/>
```
### Error with Context
```typescript
<ErrorDisplay
error={error}
context={{
action: 'fetching tracks',
resource: 'tracks',
}}
onRetry={handleRetry}
showDetails={isDev}
/>
```
### Banner Error
```typescript
<ErrorDisplay
error={error}
variant="banner"
onDismiss={() => setError(null)}
/>
```
### Modal Error
```typescript
<ErrorDisplay
error={error}
variant="modal"
onRetry={handleRetry}
onDismiss={() => setShowError(false)}
/>
```
### Error with Custom Actions
```typescript
<ErrorDisplay
error={error}
onRetry={handleRetry}
actions={[
{
label: 'Report Issue',
onClick: () => reportIssue(error),
variant: 'outline',
},
]}
/>
```
## Integration Points
### Replace Toast Errors
**Before**:
```typescript
toast.error('Failed to fetch tracks');
```
**After**:
```typescript
<ErrorDisplay
error={error}
variant="inline"
onRetry={() => refetch()}
/>
```
### Replace Inline Error Divs
**Before**:
```typescript
{error && (
<div className="text-red-500">{error.message}</div>
)}
```
**After**:
```typescript
{error && (
<ErrorDisplay
error={error}
variant="inline"
size="sm"
/>
)}
```
## Accessibility
- **ARIA labels**: `role="alert"`, `aria-live="polite"`
- **Keyboard navigation**: Tab to retry/dismiss buttons
- **Screen reader support**: Announces error message
- **Focus management**: Focuses retry button on mount
## Validation
✅ Component API designed
✅ Props interface defined
✅ Error type handling specified
✅ Usage examples provided
✅ Integration points identified
⏭️ Next: Action 3.1.1.2 - Implement ErrorDisplay component