All files / frontend/src/components Alert.tsx

85.24% Statements 52/61
16.66% Branches 1/6
0% Functions 0/1
85.24% Lines 52/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 821x 1x 1x 1x                   1x 1x 1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x   1x         1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x                 1x   1x 1x   1x   1x  
import React from 'react';
import classNames from 'classnames';
import { AlertCircle, CheckCircle, AlertTriangle, Info, X } from 'lucide-react';
import './Alert.css';
 
interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
    variant?: 'info' | 'success' | 'warning' | 'error';
    title?: string;
    message: string;
    closeable?: boolean;
    onClose?: () => void;
}
 
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
    ({ variant = 'info', title, message, closeable = false, onClose, className, ...props }, ref) => {
        const [isVisible, setIsVisible] = React.useState(true);
 
        const variantConfig = {
            info: {
                icon: Info,
                role: 'status',
            },
            success: {
                icon: CheckCircle,
                role: 'status',
            },
            warning: {
                icon: AlertTriangle,
                role: 'alert',
            },
            error: {
                icon: AlertCircle,
                role: 'alert',
            },
        };
 
        const config = variantConfig[variant];
        const Icon = config.icon;
 
        const handleClose = () => {
            setIsVisible(false);
            onClose?.();
        };
 
        if (!isVisible) return null;
 
        return (
            <div
                ref={ref}
                className={classNames(
                    'alert',
                    `alert-${variant}`,
                    className
                )}
                role={config.role}
                aria-live={variant === 'error' || variant === 'warning' ? 'assertive' : 'polite'}
                aria-atomic="true"
                {...props}
            >
                <Icon className="alert-icon" size={20} aria-hidden="true" />
                <div className="alert-content">
                    {title && <h3 className="alert-title">{title}</h3>}
                    <p className="alert-message">{message}</p>
                </div>
                {closeable && (
                    <button
                        onClick={handleClose}
                        className="alert-close-button"
                        aria-label={`Close ${variant} alert: ${title || message}`}
                    >
                        <X size={18} aria-hidden="true" />
                    </button>
                )}
            </div>
        );
    }
);
 
Alert.displayName = 'Alert';
 
export default Alert;