diff --git a/src/components/DeleteModal.test.tsx b/src/components/DeleteModal.test.tsx
new file mode 100644
index 0000000..24fda81
--- /dev/null
+++ b/src/components/DeleteModal.test.tsx
@@ -0,0 +1,42 @@
+import { fireEvent, render, screen } from '@testing-library/react'
+import { describe, expect, it, vi } from 'vitest'
+import DeleteModal from './DeleteModal'
+
+describe('DeleteModal', () => {
+ it('renders nothing when closed', () => {
+ render()
+
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
+ })
+
+ it('renders the dialog with the note title when open', () => {
+ render()
+
+ expect(screen.getByRole('dialog', { name: 'Supprimer la note ?' })).toBeInTheDocument()
+ expect(screen.getByText(/Courses/)).toBeInTheDocument()
+ })
+
+ it('shows a placeholder when the note has no title', () => {
+ render()
+
+ expect(screen.getByText(/Sans titre/)).toBeInTheDocument()
+ })
+
+ it('calls onConfirm when the confirm button is clicked', () => {
+ const onConfirm = vi.fn()
+ render()
+
+ fireEvent.click(screen.getByRole('button', { name: 'Supprimer' }))
+
+ expect(onConfirm).toHaveBeenCalled()
+ })
+
+ it('calls onCancel when the cancel button is clicked', () => {
+ const onCancel = vi.fn()
+ render()
+
+ fireEvent.click(screen.getByRole('button', { name: 'Annuler' }))
+
+ expect(onCancel).toHaveBeenCalled()
+ })
+})
diff --git a/src/components/DeleteModal.tsx b/src/components/DeleteModal.tsx
new file mode 100644
index 0000000..d44c702
--- /dev/null
+++ b/src/components/DeleteModal.tsx
@@ -0,0 +1,44 @@
+interface DeleteModalProps {
+ open: boolean
+ title: string
+ onConfirm: () => void
+ onCancel: () => void
+}
+
+function DeleteModal({ open, title, onConfirm, onCancel }: DeleteModalProps) {
+ if (!open) return null
+
+ return (
+
+
+
+ Supprimer la note ?
+
+
{title || 'Sans titre'} sera définitivement supprimée.
+
+
+
+
+
+
+ )
+}
+
+export default DeleteModal
diff --git a/src/components/NoteEditor.test.tsx b/src/components/NoteEditor.test.tsx
index 58b44e9..22edaca 100644
--- a/src/components/NoteEditor.test.tsx
+++ b/src/components/NoteEditor.test.tsx
@@ -7,8 +7,9 @@ const note: Note = { id: 42, title: 'Courses', content: 'Lait\nOeufs' }
const renderEditor = (overrides: Partial = {}) => {
const onUpdate = vi.fn()
- render()
- return { onUpdate }
+ const onDelete = vi.fn()
+ render()
+ return { onUpdate, onDelete }
}
describe('NoteEditor', () => {
@@ -70,4 +71,33 @@ describe('NoteEditor', () => {
expect(onUpdate).not.toHaveBeenCalled()
expect(screen.getByText('Courses')).toBeInTheDocument()
})
+
+ it('opens the delete confirmation when the × button is clicked', () => {
+ renderEditor()
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
+
+ fireEvent.click(screen.getByRole('button', { name: 'Supprimer la note' }))
+
+ expect(screen.getByRole('dialog', { name: 'Supprimer la note ?' })).toBeInTheDocument()
+ })
+
+ it('deletes the note after confirmation', () => {
+ const { onDelete } = renderEditor()
+
+ fireEvent.click(screen.getByRole('button', { name: 'Supprimer la note' }))
+ fireEvent.click(screen.getByRole('button', { name: 'Supprimer' }))
+
+ expect(onDelete).toHaveBeenCalledWith(42)
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
+ })
+
+ it('does not delete when the confirmation is cancelled', () => {
+ const { onDelete } = renderEditor()
+
+ fireEvent.click(screen.getByRole('button', { name: 'Supprimer la note' }))
+ fireEvent.click(screen.getByRole('button', { name: 'Annuler' }))
+
+ expect(onDelete).not.toHaveBeenCalled()
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
+ })
})
diff --git a/src/components/NoteEditor.tsx b/src/components/NoteEditor.tsx
index 9769b50..cb73a25 100644
--- a/src/components/NoteEditor.tsx
+++ b/src/components/NoteEditor.tsx
@@ -1,13 +1,16 @@
import { useState } from 'react'
import type { Note } from '../types'
+import DeleteModal from './DeleteModal'
interface NoteEditorProps {
note: Note
onUpdate: (id: number, patch: Partial>) => void
+ onDelete: (id: number) => void
}
-function NoteEditor({ note, onUpdate }: NoteEditorProps) {
+function NoteEditor({ note, onUpdate, onDelete }: NoteEditorProps) {
const [editing, setEditing] = useState(false)
+ const [confirmDelete, setConfirmDelete] = useState(false)
const [title, setTitle] = useState(note.title)
const [content, setContent] = useState(note.content)
@@ -71,7 +74,17 @@ function NoteEditor({ note, onUpdate }: NoteEditorProps) {
return (
- {note.title || 'Sans titre'}
+
+
{note.title || 'Sans titre'}
+
+
{note.content || Note vide}
@@ -84,6 +97,15 @@ function NoteEditor({ note, onUpdate }: NoteEditorProps) {
Éditer
+ {
+ setConfirmDelete(false)
+ onDelete(note.id)
+ }}
+ onCancel={() => setConfirmDelete(false)}
+ />
)
}