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

6.3 KiB

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

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:

    new Error('Failed to fetch tracks');
    
    • Message: error.message
    • Stack: Available in details (dev mode)
  2. ApiError (from generated types):

    { code: 404, message: 'Track not found', ... }
    
    • Message: error.message
    • Code: error.code
    • Status: error.status
  3. String:

    'Network error occurred';
    
    • Message: The string itself
  4. Axios Error:

    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

<ErrorDisplay
  error={error}
  onRetry={() => refetch()}
/>

Error with Context

<ErrorDisplay
  error={error}
  context={{
    action: 'fetching tracks',
    resource: 'tracks',
  }}
  onRetry={handleRetry}
  showDetails={isDev}
/>

Banner Error

<ErrorDisplay
  error={error}
  variant="banner"
  onDismiss={() => setError(null)}
/>

Modal Error

<ErrorDisplay
  error={error}
  variant="modal"
  onRetry={handleRetry}
  onDismiss={() => setShowError(false)}
/>

Error with Custom Actions

<ErrorDisplay
  error={error}
  onRetry={handleRetry}
  actions={[
    {
      label: 'Report Issue',
      onClick: () => reportIssue(error),
      variant: 'outline',
    },
  ]}
/>

Integration Points

Replace Toast Errors

Before:

toast.error('Failed to fetch tracks');

After:

<ErrorDisplay
  error={error}
  variant="inline"
  onRetry={() => refetch()}
/>

Replace Inline Error Divs

Before:

{error && (
  <div className="text-red-500">{error.message}</div>
)}

After:

{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