feat: add RGPD info popup with header trigger button
This commit is contained in:
1 parent
e5220a4c4e
commit
7655cb5b69
4 files changed
+86
No files matched your search
+12
@@ -4,11 +4,13 @@ import { useNotes } from './hooks/useNotes'
|
|||||||
import NoteList from './components/NoteList'
|
import NoteList from './components/NoteList'
|
||||||
import NoteEditor from './components/NoteEditor'
|
import NoteEditor from './components/NoteEditor'
|
||||||
import FloatingButton from './components/FloatingButton'
|
import FloatingButton from './components/FloatingButton'
|
||||||
|
import RgpdInfo from './components/RgpdInfo'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { notes, addNote, updateNote, deleteNote } = useNotes()
|
const { notes, addNote, updateNote, deleteNote } = useNotes()
|
||||||
const [selectedId, setSelectedId] = useState<number | null>(null)
|
const [selectedId, setSelectedId] = useState<number | null>(null)
|
||||||
const [creating, setCreating] = useState(false)
|
const [creating, setCreating] = useState(false)
|
||||||
|
const [showRgpd, setShowRgpd] = useState(false)
|
||||||
|
|
||||||
const selectedNote =
|
const selectedNote =
|
||||||
selectedId === null ? null : (notes.find((note) => note.id === selectedId) ?? null)
|
selectedId === null ? null : (notes.find((note) => note.id === selectedId) ?? null)
|
||||||
@@ -51,6 +53,14 @@ function App() {
|
|||||||
</button>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
<h1 className="text-lg font-semibold">Notes</h1>
|
<h1 className="text-lg font-semibold">Notes</h1>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowRgpd(true)}
|
||||||
|
aria-label="Informations sur la protection des données"
|
||||||
|
className="ml-auto flex items-center justify-center w-11 h-11 rounded-full text-xl font-semibold text-gray-700 hover:bg-gray-100 active:bg-gray-200"
|
||||||
|
>
|
||||||
|
?
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -76,6 +86,8 @@ function App() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{!creating && !selectedNote && <FloatingButton onClick={createNote} />}
|
{!creating && !selectedNote && <FloatingButton onClick={createNote} />}
|
||||||
|
|
||||||
|
<RgpdInfo open={showRgpd} onClose={() => setShowRgpd(false)} />
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
import RgpdInfo from './RgpdInfo'
|
||||||
|
|
||||||
|
describe('RgpdInfo', () => {
|
||||||
|
it('renders nothing when closed', () => {
|
||||||
|
const { container } = render(<RgpdInfo open={false} onClose={vi.fn()} />)
|
||||||
|
|
||||||
|
expect(container).toBeEmptyDOMElement()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the GDPR information when open', () => {
|
||||||
|
render(<RgpdInfo open onClose={vi.fn()} />)
|
||||||
|
|
||||||
|
expect(screen.getByRole('dialog', { name: 'Protection des données (RGPD)' })).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('localStorage', { exact: false })).toBeInTheDocument()
|
||||||
|
expect(
|
||||||
|
screen.getByText(/ne sont jamais partagées avec des tiers/i),
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(screen.getByText(/aucune transmission vers un serveur externe/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls onClose when the close button is clicked', () => {
|
||||||
|
const onClose = vi.fn()
|
||||||
|
render(<RgpdInfo open onClose={onClose} />)
|
||||||
|
|
||||||
|
screen.getByRole('button', { name: 'Fermer' }).click()
|
||||||
|
|
||||||
|
expect(onClose).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import type { RgpdInfoProps } from '../interfaces/rgpd-info'
|
||||||
|
|
||||||
|
function RgpdInfo({ open, onClose }: RgpdInfoProps) {
|
||||||
|
if (!open) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
|
||||||
|
<div
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="rgpd-title"
|
||||||
|
className="flex w-full max-w-sm flex-col gap-4 rounded-xl bg-white p-5 shadow-xl"
|
||||||
|
>
|
||||||
|
<h2 id="rgpd-title" className="text-lg font-semibold">
|
||||||
|
Protection des données (RGPD)
|
||||||
|
</h2>
|
||||||
|
<ul className="flex list-disc flex-col gap-2 pl-5 text-sm text-gray-600">
|
||||||
|
<li>
|
||||||
|
Vos données sont stockées uniquement dans le
|
||||||
|
<span className="font-medium text-gray-800"> stockage local</span> de votre navigateur.
|
||||||
|
</li>
|
||||||
|
<li>Elles ne sont jamais partagées avec des tiers.</li>
|
||||||
|
<li>Aucune transmission vers un serveur externe.</li>
|
||||||
|
</ul>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onClose}
|
||||||
|
className="rounded-lg bg-blue-700 px-5 py-3 text-white hover:bg-blue-800 active:bg-blue-900"
|
||||||
|
>
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RgpdInfo
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface RgpdInfoProps {
|
||||||
|
open: boolean
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
Reference in new issue
Block a user