From a461b62dd3cb1718749f05c17ff536f722e57bfb Mon Sep 17 00:00:00 2001 From: Amit Haridas Date: Fri, 5 Jun 2026 19:01:06 +0530 Subject: [PATCH] feat(renderer): ConfirmDialog (generic title/body/onConfirm) --- .../components/modals/ConfirmDialog.tsx | 36 ++++++++++++++++ tests/component/modals/ConfirmDialog.test.tsx | 43 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 src/renderer/components/modals/ConfirmDialog.tsx create mode 100644 tests/component/modals/ConfirmDialog.test.tsx diff --git a/src/renderer/components/modals/ConfirmDialog.tsx b/src/renderer/components/modals/ConfirmDialog.tsx new file mode 100644 index 0000000..f082d5f --- /dev/null +++ b/src/renderer/components/modals/ConfirmDialog.tsx @@ -0,0 +1,36 @@ +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { useAppStore, type ConfirmProps } from '@/stores/app-store'; + +export function ConfirmDialog(props: ConfirmProps) { + const closeModal = useAppStore((s) => s.closeModal); + const { title, body, confirmLabel = 'Confirm', cancelLabel = 'Cancel', destructive, onConfirm, onCancel } = props; + + const handleConfirm = async () => { + await onConfirm(); + closeModal(); + }; + const handleCancel = () => { + onCancel?.(); + closeModal(); + }; + + return ( + !o && handleCancel()}> + + + {title} + {body} + + + + + + + + ); +} \ No newline at end of file diff --git a/tests/component/modals/ConfirmDialog.test.tsx b/tests/component/modals/ConfirmDialog.test.tsx new file mode 100644 index 0000000..89790ac --- /dev/null +++ b/tests/component/modals/ConfirmDialog.test.tsx @@ -0,0 +1,43 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { ConfirmDialog } from '@/components/modals/ConfirmDialog'; +import { useAppStore } from '@/stores/app-store'; + +describe('ConfirmDialog', () => { + beforeEach(() => { + localStorage.clear(); + useAppStore.setState({ modal: { kind: null } } as any); + }); + + it('renders title, body, and confirm/cancel labels', () => { + render( + {}} + /> + ); + expect(screen.getByText(/delete file/i)).toBeInTheDocument(); + expect(screen.getByText(/cannot be undone/i)).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /^delete$/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument(); + }); + + it('calls onConfirm and closes on confirm click', async () => { + const onConfirm = vi.fn(); + render(); + await userEvent.click(screen.getByRole('button', { name: /confirm/i })); + expect(onConfirm).toHaveBeenCalledTimes(1); + expect(useAppStore.getState().modal).toEqual({ kind: null }); + }); + + it('destructive variant uses destructive button class', () => { + render( + {}} /> + ); + const btn = screen.getByRole('button', { name: /confirm/i }); + expect(btn.className).toContain('bg-destructive'); + }); +}); \ No newline at end of file