feat(PBI-49): add BEM sub-element data-debug-id to admin, auth, dialogs, entity-dialog, mobile, split-pane

This commit is contained in:
Scrum4Me Agent 2026-05-09 22:35:56 +02:00
parent c11b5f3b4c
commit 68a2439b72
9 changed files with 25 additions and 16 deletions

View file

@ -101,7 +101,7 @@ function JobRow({ job }: { job: Job }) {
function StatusTable({ jobs }: { jobs: Job[] }) {
return (
<Table>
<Table data-debug-id="admin-jobs-table__table">
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
@ -172,7 +172,7 @@ function CostRow({ job }: { job: Job }) {
function CostsTable({ jobs }: { jobs: Job[] }) {
return (
<Table>
<Table data-debug-id="admin-jobs-table__table">
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
@ -204,8 +204,8 @@ export function JobsTable({ jobs }: { jobs: Job[] }) {
const [view, setView] = useState<'status' | 'costs'>('status')
return (
<div className="space-y-3" {...debugProps('jobs-table', 'JobsTable', 'components/admin/jobs-table.tsx')}>
<div className="flex gap-1">
<div className="space-y-3" {...debugProps('admin-jobs-table', 'JobsTable', 'components/admin/jobs-table.tsx')}>
<div className="flex gap-1" data-debug-id="admin-jobs-table__header">
<Button
size="sm"
variant={view === 'status' ? 'default' : 'outline'}

View file

@ -79,8 +79,8 @@ function DeleteDialog({ product }: { product: Product }) {
export function ProductsTable({ products }: { products: Product[] }) {
return (
<Table {...debugProps('products-table', 'ProductsTable', 'components/admin/products-table.tsx')}>
<TableHeader>
<Table {...debugProps('admin-products-table', 'ProductsTable', 'components/admin/products-table.tsx')}>
<TableHeader data-debug-id="admin-products-table__header">
<TableRow>
<TableHead>Naam</TableHead>
<TableHead>Eigenaar</TableHead>
@ -91,7 +91,7 @@ export function ProductsTable({ products }: { products: Product[] }) {
<TableHead className="text-right">Acties</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableBody data-debug-id="admin-products-table__table">
{products.length === 0 && (
<TableRow>
<TableCell colSpan={7} className="text-center text-muted-foreground py-8">

View file

@ -191,8 +191,8 @@ export function UsersTable({
currentUserId: string
}) {
return (
<Table {...debugProps('users-table', 'UsersTable', 'components/admin/users-table.tsx')}>
<TableHeader>
<Table {...debugProps('admin-users-table', 'UsersTable', 'components/admin/users-table.tsx')}>
<TableHeader data-debug-id="admin-users-table__header">
<TableRow>
<TableHead>Gebruiker</TableHead>
<TableHead>Email</TableHead>
@ -202,7 +202,7 @@ export function UsersTable({
<TableHead className="text-right">Acties</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableBody data-debug-id="admin-users-table__table">
{users.map(user => (
<TableRow key={user.id}>
<TableCell className="font-medium">{user.username}</TableCell>

View file

@ -4,13 +4,14 @@ import { useActionState } from 'react'
import { useFormStatus } from 'react-dom'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { debugProps } from '@/lib/debug'
type ActionResult = { error: string | Record<string, string[]> } | undefined
function SubmitButton({ label }: { label: string }) {
const { pending } = useFormStatus()
return (
<Button type="submit" className="w-full" disabled={pending}>
<Button type="submit" className="w-full" disabled={pending} data-debug-id="auth-form__submit">
{pending ? 'Even wachten…' : label}
</Button>
)
@ -34,7 +35,7 @@ export function AuthForm({ action, submitLabel }: AuthFormProps) {
const errorMessage = getErrorMessage(state)
return (
<form action={formAction} className="space-y-4">
<form action={formAction} className="space-y-4" {...debugProps('auth-form', 'AuthForm', 'components/auth/auth-form.tsx')}>
<div className="space-y-2">
<label htmlFor="username" className="text-sm font-medium text-foreground">
Gebruikersnaam
@ -48,6 +49,7 @@ export function AuthForm({ action, submitLabel }: AuthFormProps) {
minLength={3}
placeholder="jouw-naam"
className="bg-input-background border-border focus-visible:ring-primary"
data-debug-id="auth-form__username"
/>
</div>
@ -64,6 +66,7 @@ export function AuthForm({ action, submitLabel }: AuthFormProps) {
minLength={8}
placeholder="••••••••"
className="bg-input-background border-border focus-visible:ring-primary"
data-debug-id="auth-form__password"
/>
</div>

View file

@ -172,6 +172,7 @@ export function ProductDialog(props: Props) {
id="product-form"
onSubmit={form.handleSubmit(onSubmit)}
className={entityDialogBodyClasses}
data-debug-id="product-dialog__content"
>
<div className="grid gap-1.5">
<label htmlFor="product-name" className="text-sm font-medium">
@ -296,7 +297,7 @@ export function ProductDialog(props: Props) {
Annuleren
</Button>
<DemoTooltip show={isDemo}>
<Button type="submit" form="product-form" disabled={isPending || isDemo}>
<Button type="submit" form="product-form" disabled={isPending || isDemo} data-debug-id="product-dialog__submit">
{isPending ? '…' : mode === 'edit' ? 'Opslaan' : 'Aanmaken'}
</Button>
</DemoTooltip>

View file

@ -1,6 +1,7 @@
'use client'
import { useState } from 'react'
import { debugProps } from '@/lib/debug'
import {
AlertDialog,
AlertDialogContent,
@ -33,7 +34,7 @@ export function DirtyCloseGuard({ isDirty, onConfirm, children }: DirtyCloseGuar
<>
{children(attemptClose)}
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogContent size="sm">
<AlertDialogContent size="sm" {...debugProps('dirty-close-guard', 'DirtyCloseGuard', 'components/entity-dialog/dirty-close-guard.tsx')}>
<AlertDialogHeader>
<AlertDialogTitle>Wijzigingen niet opgeslagen</AlertDialogTitle>
<AlertDialogDescription>
@ -41,11 +42,12 @@ export function DirtyCloseGuard({ isDirty, onConfirm, children }: DirtyCloseGuar
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setOpen(false)}>
<AlertDialogCancel data-debug-id="dirty-close-guard__cancel" onClick={() => setOpen(false)}>
Blijven
</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
data-debug-id="dirty-close-guard__confirm"
onClick={() => { setOpen(false); onConfirm() }}
>
Weggooien

View file

@ -26,7 +26,7 @@ export function LandscapeGuard({ children }: { children: React.ReactNode }) {
{...debugProps('landscape-guard', 'LandscapeGuard', 'components/mobile/landscape-guard.tsx')}
>
<RotateCw className="size-12 text-primary" />
<p className="text-base font-medium text-center">Draai je telefoon naar landscape</p>
<p className="text-base font-medium text-center" data-debug-id="landscape-guard__title">Draai je telefoon naar landscape</p>
</div>
)}
</>

View file

@ -54,6 +54,7 @@ export function MobileTabBar({ activeProductId }: MobileTabBarProps) {
href={tab.href}
aria-label={tab.label}
aria-current={active ? 'page' : undefined}
data-debug-id={`mobile-tab-bar__tab-${tab.label.toLowerCase()}`}
className={cn(
'flex-1 h-14 flex items-center justify-center transition-colors',
active

View file

@ -163,6 +163,7 @@ export function SplitPane({
<Fragment key={i}>
{i > 0 && (
<div
data-debug-id="split-pane__divider"
onMouseDown={() => setDragging(i - 1)}
className={cn(
'w-1 shrink-0 bg-border hover:bg-primary transition-colors cursor-col-resize',
@ -173,6 +174,7 @@ export function SplitPane({
<div
className="flex flex-col overflow-hidden"
style={i === n - 1 ? { flex: 1 } : { width: `${splits[i]}%` }}
data-debug-id={i === 0 ? 'split-pane__left' : i === n - 1 ? 'split-pane__right' : undefined}
>
{pane}
</div>