fix(web): reemplazar <select> nativos por shadcn Select (dark mode compat) — ADM-001
Reemplaza 13 <select>/<option> nativos en 8 archivos por el componente shadcn Select (Radix UI). Los selects nativos ignoraban los tokens del design system en dark mode, causando texto invisible. Se agrega mock de pointer capture APIs en test setup para compatibilidad de Radix con jsdom.
This commit is contained in:
@@ -15,6 +15,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
||||||
import type { MedioDetail } from '../types'
|
import type { MedioDetail } from '../types'
|
||||||
|
|
||||||
@@ -136,22 +143,24 @@ export function MedioForm({ initialData, isPending, error, onSubmit }: MedioForm
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Tipo</FormLabel>
|
<FormLabel>Tipo</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value ? String(field.value) : ''}
|
||||||
{...field}
|
onValueChange={(v) => field.onChange(v === '' ? '' : Number(v))}
|
||||||
value={field.value ?? ''}
|
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
aria-label="Tipo"
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">Seleccioná un tipo</option>
|
<FormControl>
|
||||||
{TIPO_MEDIO_OPTIONS.map((o) => (
|
<SelectTrigger className="w-full" aria-label="Tipo">
|
||||||
<option key={o.value} value={o.value}>
|
<SelectValue placeholder="Seleccioná un tipo" />
|
||||||
{o.label}
|
</SelectTrigger>
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{TIPO_MEDIO_OPTIONS.map((o) => (
|
||||||
|
<SelectItem key={o.value} value={String(o.value)}>
|
||||||
|
{o.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import { Input } from '@/components/ui/input'
|
|||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { CanPerform } from '@/components/auth/CanPerform'
|
import { CanPerform } from '@/components/auth/CanPerform'
|
||||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { MediosTable } from '../components/MediosTable'
|
import { MediosTable } from '../components/MediosTable'
|
||||||
import { useMediosList } from '../hooks/useMediosList'
|
import { useMediosList } from '../hooks/useMediosList'
|
||||||
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
||||||
@@ -70,28 +77,36 @@ export function MediosListPage() {
|
|||||||
aria-label="Buscar medios"
|
aria-label="Buscar medios"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Tipo"
|
value={tipo !== undefined ? String(tipo) : '__all__'}
|
||||||
onChange={(e) => handleTipoChange(e.target.value)}
|
onValueChange={(v) => handleTipoChange(v === '__all__' ? '' : v)}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos los tipos</option>
|
<SelectTrigger className="h-9 w-40" aria-label="Tipo">
|
||||||
|
<SelectValue placeholder="Todos los tipos" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos los tipos</SelectItem>
|
||||||
{TIPO_MEDIO_OPTIONS.map((o) => (
|
{TIPO_MEDIO_OPTIONS.map((o) => (
|
||||||
<option key={o.value} value={o.value}>
|
<SelectItem key={o.value} value={String(o.value)}>
|
||||||
{o.label}
|
{o.label}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Estado"
|
value={activo !== undefined ? String(activo) : '__all__'}
|
||||||
onChange={(e) => handleActivoChange(e.target.value)}
|
onValueChange={(v) => handleActivoChange(v === '__all__' ? '' : v)}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos</option>
|
<SelectTrigger className="h-9 w-32" aria-label="Estado">
|
||||||
<option value="true">Activos</option>
|
<SelectValue placeholder="Todos" />
|
||||||
<option value="false">Inactivos</option>
|
</SelectTrigger>
|
||||||
</select>
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos</SelectItem>
|
||||||
|
<SelectItem value="true">Activos</SelectItem>
|
||||||
|
<SelectItem value="false">Inactivos</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/components/ui/card'
|
} from '@/components/ui/card'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useRoles } from '../../roles/hooks/useRoles'
|
import { useRoles } from '../../roles/hooks/useRoles'
|
||||||
import { RolPermisosEditor } from '../components/RolPermisosEditor'
|
import { RolPermisosEditor } from '../components/RolPermisosEditor'
|
||||||
|
|
||||||
@@ -28,28 +35,28 @@ export function RolPermisosPage() {
|
|||||||
<CardContent className="space-y-6">
|
<CardContent className="space-y-6">
|
||||||
{/* Selector de rol */}
|
{/* Selector de rol */}
|
||||||
<div className="flex flex-col gap-1 max-w-xs">
|
<div className="flex flex-col gap-1 max-w-xs">
|
||||||
<label
|
<label className="text-sm font-medium text-foreground">
|
||||||
htmlFor="rol-selector"
|
|
||||||
className="text-sm font-medium text-foreground"
|
|
||||||
>
|
|
||||||
Rol
|
Rol
|
||||||
</label>
|
</label>
|
||||||
{loadingRoles ? (
|
{loadingRoles ? (
|
||||||
<p className="text-sm text-muted-foreground">Cargando roles...</p>
|
<p className="text-sm text-muted-foreground">Cargando roles...</p>
|
||||||
) : (
|
) : (
|
||||||
<select
|
<Select
|
||||||
id="rol-selector"
|
value={selectedRol ?? '__none__'}
|
||||||
value={selectedRol ?? ''}
|
onValueChange={(v) => setSelectedRol(v === '__none__' ? null : v)}
|
||||||
onChange={(e) => setSelectedRol(e.target.value || null)}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">— Seleccioná un rol —</option>
|
<SelectTrigger className="w-full" aria-label="Rol">
|
||||||
|
<SelectValue placeholder="— Seleccioná un rol —" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="__none__">— Seleccioná un rol —</SelectItem>
|
||||||
{rolesActivos.map((r) => (
|
{rolesActivos.map((r) => (
|
||||||
<option key={r.codigo} value={r.codigo}>
|
<SelectItem key={r.codigo} value={r.codigo}>
|
||||||
{r.nombre} ({r.codigo})
|
{r.nombre} ({r.codigo})
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
||||||
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
||||||
import type { SeccionDetail, TipoSeccion } from '../types'
|
import type { SeccionDetail, TipoSeccion } from '../types'
|
||||||
@@ -100,22 +107,24 @@ export function SeccionForm({ initialData, isPending, error, onSubmit }: Seccion
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Medio</FormLabel>
|
<FormLabel>Medio</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value ? String(field.value) : ''}
|
||||||
{...field}
|
onValueChange={(v) => field.onChange(Number(v))}
|
||||||
value={field.value ?? ''}
|
|
||||||
disabled={isPending || isEdit}
|
disabled={isPending || isEdit}
|
||||||
aria-label="Medio"
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">Seleccioná un medio</option>
|
<FormControl>
|
||||||
{medios.map((m) => (
|
<SelectTrigger className="w-full" aria-label="Medio">
|
||||||
<option key={m.id} value={m.id}>
|
<SelectValue placeholder="Seleccioná un medio" />
|
||||||
{m.nombre}
|
</SelectTrigger>
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{medios.map((m) => (
|
||||||
|
<SelectItem key={m.id} value={String(m.id)}>
|
||||||
|
{m.nombre}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -165,22 +174,24 @@ export function SeccionForm({ initialData, isPending, error, onSubmit }: Seccion
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Tipo</FormLabel>
|
<FormLabel>Tipo</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
|
||||||
{...field}
|
|
||||||
value={field.value ?? ''}
|
value={field.value ?? ''}
|
||||||
|
onValueChange={(v) => field.onChange(v as TipoSeccion)}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
aria-label="Tipo de sección"
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">Seleccioná un tipo</option>
|
<FormControl>
|
||||||
{TIPO_SECCION_OPTIONS.map((o) => (
|
<SelectTrigger className="w-full" aria-label="Tipo de sección">
|
||||||
<option key={o.value} value={o.value}>
|
<SelectValue placeholder="Seleccioná un tipo" />
|
||||||
{o.label}
|
</SelectTrigger>
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{TIPO_SECCION_OPTIONS.map((o) => (
|
||||||
|
<SelectItem key={o.value} value={o.value}>
|
||||||
|
{o.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
||||||
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
||||||
import type { TipoSeccion } from '../types'
|
import type { TipoSeccion } from '../types'
|
||||||
@@ -40,51 +47,56 @@ export function SeccionesFilters({
|
|||||||
aria-label="Buscar secciones"
|
aria-label="Buscar secciones"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Medio"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => onMedioIdChange(v === '__all__' ? undefined : Number(v))}
|
||||||
const v = e.target.value
|
|
||||||
onMedioIdChange(v === '' ? undefined : Number(v))
|
|
||||||
}}
|
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos los medios</option>
|
<SelectTrigger className="h-9 w-44" aria-label="Medio">
|
||||||
|
<SelectValue placeholder="Todos los medios" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos los medios</SelectItem>
|
||||||
{medios.map((m) => (
|
{medios.map((m) => (
|
||||||
<option key={m.id} value={m.id}>
|
<SelectItem key={m.id} value={String(m.id)}>
|
||||||
{m.nombre}
|
{m.nombre}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Tipo de sección"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => onTipoChange(v === '__all__' ? undefined : (v as TipoSeccion))}
|
||||||
const v = e.target.value
|
|
||||||
onTipoChange(v === '' ? undefined : (v as TipoSeccion))
|
|
||||||
}}
|
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos los tipos</option>
|
<SelectTrigger className="h-9 w-44" aria-label="Tipo de sección">
|
||||||
|
<SelectValue placeholder="Todos los tipos" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos los tipos</SelectItem>
|
||||||
{TIPO_SECCION_OPTIONS.map((o) => (
|
{TIPO_SECCION_OPTIONS.map((o) => (
|
||||||
<option key={o.value} value={o.value}>
|
<SelectItem key={o.value} value={o.value}>
|
||||||
{o.label}
|
{o.label}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Estado"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => {
|
||||||
const v = e.target.value
|
if (v === '__all__') onActivoChange(undefined)
|
||||||
if (v === '') onActivoChange(undefined)
|
|
||||||
else onActivoChange(v === 'true')
|
else onActivoChange(v === 'true')
|
||||||
}}
|
}}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos</option>
|
<SelectTrigger className="h-9 w-32" aria-label="Estado">
|
||||||
<option value="true">Activos</option>
|
<SelectValue placeholder="Todos" />
|
||||||
<option value="false">Inactivos</option>
|
</SelectTrigger>
|
||||||
</select>
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos</SelectItem>
|
||||||
|
<SelectItem value="true">Activos</SelectItem>
|
||||||
|
<SelectItem value="false">Inactivos</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useCreateUser } from '../hooks/useCreateUser'
|
import { useCreateUser } from '../hooks/useCreateUser'
|
||||||
import { useRolesForSelect } from '../hooks/useRolesForSelect'
|
import { useRolesForSelect } from '../hooks/useRolesForSelect'
|
||||||
import type { CreatedUserDto } from '../api/createUser'
|
import type { CreatedUserDto } from '../api/createUser'
|
||||||
@@ -202,23 +209,26 @@ export function UserForm({ onSuccess }: UserFormProps) {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Rol</FormLabel>
|
<FormLabel>Rol</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value}
|
||||||
{...field}
|
onValueChange={field.onChange}
|
||||||
disabled={disabled || rolesError}
|
disabled={disabled || !!rolesError}
|
||||||
aria-label="Rol"
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">
|
<FormControl>
|
||||||
{rolesLoading ? 'Cargando roles...' : 'Seleccioná un rol'}
|
<SelectTrigger className="w-full" aria-label="Rol">
|
||||||
</option>
|
<SelectValue
|
||||||
{rolOptions.map((r) => (
|
placeholder={rolesLoading ? 'Cargando roles...' : 'Seleccioná un rol'}
|
||||||
<option key={r.codigo} value={r.codigo}>
|
/>
|
||||||
{r.nombre}
|
</SelectTrigger>
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{rolOptions.map((r) => (
|
||||||
|
<SelectItem key={r.codigo} value={r.codigo}>
|
||||||
|
{r.nombre}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
|
|
||||||
interface UsersFiltersProps {
|
interface UsersFiltersProps {
|
||||||
onRolChange: (rol: string) => void
|
onRolChange: (rol: string) => void
|
||||||
@@ -38,32 +45,39 @@ export function UsersFilters({ onRolChange, onActivoChange, onSearchChange }: Us
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Rol select */}
|
{/* Rol select */}
|
||||||
<select
|
<Select
|
||||||
aria-label="Rol"
|
defaultValue="__all__"
|
||||||
onChange={(e) => onRolChange(e.target.value)}
|
onValueChange={(v) => onRolChange(v === '__all__' ? '' : v)}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
|
<SelectTrigger className="h-9 w-40" aria-label="Rol">
|
||||||
|
<SelectValue placeholder="Todos los roles" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
{ROL_OPTIONS.map((r) => (
|
{ROL_OPTIONS.map((r) => (
|
||||||
<option key={r.value} value={r.value}>
|
<SelectItem key={r.value || '__all__'} value={r.value || '__all__'}>
|
||||||
{r.label}
|
{r.label}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
{/* Activo filter */}
|
{/* Activo filter */}
|
||||||
<select
|
<Select
|
||||||
aria-label="Estado"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => {
|
||||||
const v = e.target.value
|
if (v === '__all__') onActivoChange(undefined)
|
||||||
if (v === '') onActivoChange(undefined)
|
|
||||||
else onActivoChange(v === 'true')
|
else onActivoChange(v === 'true')
|
||||||
}}
|
}}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos</option>
|
<SelectTrigger className="h-9 w-32" aria-label="Estado">
|
||||||
<option value="true">Activos</option>
|
<SelectValue placeholder="Todos" />
|
||||||
<option value="false">Inactivos</option>
|
</SelectTrigger>
|
||||||
</select>
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos</SelectItem>
|
||||||
|
<SelectItem value="true">Activos</SelectItem>
|
||||||
|
<SelectItem value="false">Inactivos</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useUser } from '../hooks/useUser'
|
import { useUser } from '../hooks/useUser'
|
||||||
import { useUpdateUser } from '../hooks/useUpdateUser'
|
import { useUpdateUser } from '../hooks/useUpdateUser'
|
||||||
import { ResetPasswordModal } from '../components/ResetPasswordModal'
|
import { ResetPasswordModal } from '../components/ResetPasswordModal'
|
||||||
@@ -199,18 +206,22 @@ export function UserEditPage() {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Rol</FormLabel>
|
<FormLabel>Rol</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value}
|
||||||
{...field}
|
onValueChange={field.onChange}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
aria-label="Rol"
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="admin">Admin</option>
|
<FormControl>
|
||||||
<option value="cajero">Cajero</option>
|
<SelectTrigger className="w-full" aria-label="Rol">
|
||||||
<option value="reportes">Reportes</option>
|
<SelectValue placeholder="Seleccioná un rol" />
|
||||||
</select>
|
</SelectTrigger>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="admin">Admin</SelectItem>
|
||||||
|
<SelectItem value="cajero">Cajero</SelectItem>
|
||||||
|
<SelectItem value="reportes">Reportes</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -61,7 +61,11 @@ describe('CreateMedioPage', () => {
|
|||||||
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'RAD01')
|
await userEvent.type(screen.getByLabelText(/código/i), 'RAD01')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Radio AM')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Radio AM')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo/i), '2')
|
|
||||||
|
// Open Radix Select trigger and pick "Radio" (value=2)
|
||||||
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^radio$/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
||||||
|
|
||||||
await waitFor(() => expect(mockNavigate).toHaveBeenCalledWith('/admin/medios'))
|
await waitFor(() => expect(mockNavigate).toHaveBeenCalledWith('/admin/medios'))
|
||||||
@@ -81,7 +85,11 @@ describe('CreateMedioPage', () => {
|
|||||||
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'DUP01')
|
await userEvent.type(screen.getByLabelText(/código/i), 'DUP01')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Duplicado')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Duplicado')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo/i), '1')
|
|
||||||
|
// Open Radix Select trigger and pick "Diario" (value=1)
|
||||||
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^diario$/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
|
|||||||
@@ -79,7 +79,11 @@ describe('MedioForm — create mode', () => {
|
|||||||
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'DIA99')
|
await userEvent.type(screen.getByLabelText(/código/i), 'DIA99')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Diario')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Diario')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo/i), '1')
|
|
||||||
|
// Open the Radix Select trigger and pick option
|
||||||
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /diario/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
|||||||
@@ -144,8 +144,9 @@ describe('MediosListPage', () => {
|
|||||||
|
|
||||||
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
||||||
|
|
||||||
const tipoSelect = screen.getByRole('combobox', { name: /tipo/i })
|
// Open the Radix Select trigger and pick "Diario" (value=1)
|
||||||
await userEvent.selectOptions(tipoSelect, '1')
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^diario$/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const filtered = requests.find((u) => u.includes('tipo=1'))
|
const filtered = requests.find((u) => u.includes('tipo=1'))
|
||||||
|
|||||||
@@ -88,15 +88,25 @@ describe('SeccionForm — create mode', () => {
|
|||||||
const onSubmit = vi.fn()
|
const onSubmit = vi.fn()
|
||||||
renderForm({ onSubmit })
|
renderForm({ onSubmit })
|
||||||
|
|
||||||
// Wait for medios to load
|
// Open Medio trigger, wait for medios to load, then pick one
|
||||||
|
const medioTrigger = screen.getByRole('combobox', { name: /medio/i })
|
||||||
|
await userEvent.click(medioTrigger)
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: 'Diario El Día' }))
|
||||||
|
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/medio/i), '1')
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'CLAS99')
|
await userEvent.type(screen.getByLabelText(/código/i), 'CLAS99')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Sección')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Sección')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo de sección/i), 'clasificados')
|
|
||||||
|
// Open Tipo trigger and pick Clasificados
|
||||||
|
const tipoTrigger = screen.getByRole('combobox', { name: /tipo de sección/i })
|
||||||
|
await userEvent.click(tipoTrigger)
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
||||||
|
)
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: 'Clasificados' }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear sección/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear sección/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -117,8 +127,9 @@ describe('SeccionForm — edit mode', () => {
|
|||||||
renderForm({ initialData: sampleSeccion })
|
renderForm({ initialData: sampleSeccion })
|
||||||
const codigoInput = screen.getByLabelText(/código/i) as HTMLInputElement
|
const codigoInput = screen.getByLabelText(/código/i) as HTMLInputElement
|
||||||
expect(codigoInput.disabled).toBe(true)
|
expect(codigoInput.disabled).toBe(true)
|
||||||
const medioSelect = screen.getByLabelText(/medio/i) as HTMLSelectElement
|
// Radix Select trigger is a <button> — check it's disabled
|
||||||
expect(medioSelect.disabled).toBe(true)
|
const medioTrigger = screen.getByRole('combobox', { name: /medio/i })
|
||||||
|
expect(medioTrigger).toBeDisabled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('pre-fills form with initialData values', async () => {
|
it('pre-fills form with initialData values', async () => {
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ describe('SeccionesFilters', () => {
|
|||||||
it('loads medios options from API', async () => {
|
it('loads medios options from API', async () => {
|
||||||
renderFilters()
|
renderFilters()
|
||||||
|
|
||||||
|
// Open the Medio Radix Select to see options in the portal
|
||||||
|
const medioTrigger = await screen.findByRole('combobox', { name: /medio/i })
|
||||||
|
await userEvent.click(medioTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
@@ -66,34 +70,55 @@ describe('SeccionesFilters', () => {
|
|||||||
it('calls onMedioIdChange when medio is selected', async () => {
|
it('calls onMedioIdChange when medio is selected', async () => {
|
||||||
const handlers = renderFilters()
|
const handlers = renderFilters()
|
||||||
|
|
||||||
|
// Open Medio trigger, wait for options to load, then pick one
|
||||||
|
const medioTrigger = await screen.findByRole('combobox', { name: /medio/i })
|
||||||
|
await userEvent.click(medioTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/medio/i), '1')
|
await userEvent.click(screen.getByRole('option', { name: 'Diario El Día' }))
|
||||||
expect(handlers.onMedioIdChange).toHaveBeenCalledWith(1)
|
expect(handlers.onMedioIdChange).toHaveBeenCalledWith(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls onTipoChange when tipo is selected', async () => {
|
it('calls onTipoChange when tipo is selected', async () => {
|
||||||
const handlers = renderFilters()
|
const handlers = renderFilters()
|
||||||
|
|
||||||
|
// Open Tipo de sección trigger and pick an option
|
||||||
|
const tipoTrigger = screen.getByRole('combobox', { name: /tipo de sección/i })
|
||||||
|
await userEvent.click(tipoTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo de sección/i), 'clasificados')
|
await userEvent.click(screen.getByRole('option', { name: 'Clasificados' }))
|
||||||
expect(handlers.onTipoChange).toHaveBeenCalledWith('clasificados')
|
expect(handlers.onTipoChange).toHaveBeenCalledWith('clasificados')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls onActivoChange when estado is selected', async () => {
|
it('calls onActivoChange when estado is selected', async () => {
|
||||||
const handlers = renderFilters()
|
const handlers = renderFilters()
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/estado/i), 'true')
|
|
||||||
|
// Open Estado trigger and pick Activos
|
||||||
|
const estadoTrigger = screen.getByRole('combobox', { name: /estado/i })
|
||||||
|
await userEvent.click(estadoTrigger)
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByRole('option', { name: 'Activos' })).toBeInTheDocument(),
|
||||||
|
)
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: 'Activos' }))
|
||||||
expect(handlers.onActivoChange).toHaveBeenCalledWith(true)
|
expect(handlers.onActivoChange).toHaveBeenCalledWith(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders all tipo options', async () => {
|
it('renders all tipo options', async () => {
|
||||||
renderFilters()
|
renderFilters()
|
||||||
|
|
||||||
|
// Open Tipo de sección trigger to see options in portal
|
||||||
|
const tipoTrigger = screen.getByRole('combobox', { name: /tipo de sección/i })
|
||||||
|
await userEvent.click(tipoTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -107,6 +107,13 @@ describe('UserEditPage', () => {
|
|||||||
await userEvent.clear(nombreInput)
|
await userEvent.clear(nombreInput)
|
||||||
await userEvent.type(nombreInput, 'Pedro')
|
await userEvent.type(nombreInput, 'Pedro')
|
||||||
|
|
||||||
|
// Confirm the rol Select has a value by opening and re-selecting the prefilled value
|
||||||
|
// (Radix Select in jsdom needs interaction to register the item text in context)
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
await waitFor(() => expect(screen.getByRole('option', { name: /cajero/i })).toBeInTheDocument())
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /cajero/i }))
|
||||||
|
|
||||||
// Submit
|
// Submit
|
||||||
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
||||||
|
|
||||||
@@ -129,6 +136,12 @@ describe('UserEditPage', () => {
|
|||||||
|
|
||||||
await waitFor(() => expect(screen.getByDisplayValue('Juan')).toBeInTheDocument())
|
await waitFor(() => expect(screen.getByDisplayValue('Juan')).toBeInTheDocument())
|
||||||
|
|
||||||
|
// Confirm rol by opening and re-selecting the prefilled value
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
await waitFor(() => expect(screen.getByRole('option', { name: /cajero/i })).toBeInTheDocument())
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /cajero/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
|
|||||||
@@ -109,6 +109,10 @@ describe('UserForm — roles dropdown integration', () => {
|
|||||||
)
|
)
|
||||||
renderForm()
|
renderForm()
|
||||||
|
|
||||||
|
// Open the Radix Select trigger to see options in the portal
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
|
||||||
// Wait for roles to load — active options should appear.
|
// Wait for roles to load — active options should appear.
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(
|
expect(
|
||||||
@@ -132,15 +136,21 @@ describe('UserForm — roles dropdown integration', () => {
|
|||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
renderForm(onSuccess)
|
renderForm(onSuccess)
|
||||||
|
|
||||||
|
// Open trigger and wait for Cajero option to load
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await user.click(rolTrigger)
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Pick Cajero option
|
||||||
|
await user.click(screen.getByRole('option', { name: 'Cajero' }))
|
||||||
|
|
||||||
await user.type(screen.getByLabelText(/usuario/i), 'jdoe123')
|
await user.type(screen.getByLabelText(/usuario/i), 'jdoe123')
|
||||||
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
||||||
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
||||||
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
||||||
await user.selectOptions(screen.getByLabelText(/rol/i), 'cajero')
|
|
||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
||||||
|
|
||||||
@@ -176,15 +186,20 @@ describe('UserForm — backend error display', () => {
|
|||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
renderForm()
|
renderForm()
|
||||||
|
|
||||||
|
// Open trigger, wait for options, pick Cajero
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await user.click(rolTrigger)
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await user.click(screen.getByRole('option', { name: 'Cajero' }))
|
||||||
|
|
||||||
await user.type(screen.getByLabelText(/usuario/i), 'existing')
|
await user.type(screen.getByLabelText(/usuario/i), 'existing')
|
||||||
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
||||||
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
||||||
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
||||||
await user.selectOptions(screen.getByLabelText(/rol/i), 'cajero')
|
|
||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
||||||
|
|
||||||
|
|||||||
@@ -171,8 +171,10 @@ describe('UsersListPage', () => {
|
|||||||
|
|
||||||
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
||||||
|
|
||||||
const rolSelect = screen.getByRole('combobox', { name: /rol/i })
|
// Open the Radix Select trigger and pick "Admin"
|
||||||
await userEvent.selectOptions(rolSelect, 'admin')
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^admin$/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const filtered = requests.find((u) => u.includes('rol=admin'))
|
const filtered = requests.find((u) => u.includes('rol=admin'))
|
||||||
|
|||||||
@@ -1 +1,10 @@
|
|||||||
import '@testing-library/jest-dom'
|
import '@testing-library/jest-dom'
|
||||||
|
|
||||||
|
// Radix UI primitives use pointer capture APIs not available in jsdom.
|
||||||
|
// Provide no-op stubs so Radix Select/etc. work in unit tests.
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.HTMLElement.prototype.hasPointerCapture = () => false
|
||||||
|
window.HTMLElement.prototype.setPointerCapture = () => {}
|
||||||
|
window.HTMLElement.prototype.releasePointerCapture = () => {}
|
||||||
|
window.HTMLElement.prototype.scrollIntoView = () => {}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user