All files / src/components ConfirmDialog.tsx

100% Statements 10/10
100% Branches 7/7
100% Functions 3/3
100% Lines 9/9

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 82 83                                            29x 29x 29x   29x 29x     29x   2x           29x       29x                                                                                
import { useCallback, useEffect, useRef } from "react";
import { useFocusTrap } from "../hooks";
 
interface ConfirmDialogProps {
	title: string;
	message: string;
	confirmLabel?: string;
	cancelLabel?: string;
	variant?: "danger" | "default";
	onConfirm: () => void;
	onCancel: () => void;
}
 
export function ConfirmDialog({
	title,
	message,
	confirmLabel = "Confirm",
	cancelLabel = "Cancel",
	variant = "default",
	onConfirm,
	onCancel,
}: ConfirmDialogProps) {
	const cancelRef = useRef<HTMLButtonElement>(null);
	const dialogRef = useRef<HTMLDivElement>(null);
	useFocusTrap(dialogRef);
 
	useEffect(() => {
		cancelRef.current?.focus();
	}, []);
 
	const handleKeyDown = useCallback(
		(e: React.KeyboardEvent) => {
			if (e.key === "Escape") onCancel();
		},
		[onCancel],
	);
 
	const confirmClass =
		variant === "danger"
			? "bg-red-600 hover:bg-red-700 text-white"
			: "bg-stork-600 hover:bg-stork-700 text-white";
 
	return (
		<div
			ref={dialogRef}
			className="fixed inset-0 z-[60] flex items-center justify-center bg-black/40"
			role="dialog"
			aria-modal="true"
			onKeyDown={handleKeyDown}
		>
			<div
				className="bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-full max-w-sm p-6"
				aria-labelledby="confirm-title"
			>
				<h3
					id="confirm-title"
					className="text-base font-semibold text-gray-900 dark:text-gray-100 mb-2"
				>
					{title}
				</h3>
				<p className="text-sm text-gray-600 dark:text-gray-400 mb-5">{message}</p>
				<div className="flex items-center justify-end gap-2">
					<button
						ref={cancelRef}
						type="button"
						onClick={onCancel}
						className="px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors"
					>
						{cancelLabel}
					</button>
					<button
						type="button"
						onClick={onConfirm}
						className={`px-4 py-1.5 text-sm font-medium rounded-md transition-colors ${confirmClass}`}
					>
						{confirmLabel}
					</button>
				</div>
			</div>
		</div>
	);
}