accessible-search-select
v0.0.4
Published
Un componente de selección con buscador integrado, reutilizable y tipado en TypeScript. Soporta selección única o múltiple y filtrado en memoria.
Maintainers
Readme
Accessible Search Select
Un componente de selección con buscador integrado, reutilizable y tipado en TypeScript.
Soporta selección única o múltiple y filtrado en memoria.

Instalación
npm install accessible-search-select
# o
yarn add accessible-search-selectRequisitos
Esta librería utiliza React hooks, por lo que necesita que React esté instalado en tu proyecto.
npm install react react-domUso básico
import { SelectSearch } from "accessible-search-select";
interface User {
id: number;
name: string;
}
const users: User[] = [
{ id: 1, name: "Juan" },
{ id: 2, name: "María" },
{ id: 3, name: "Pedro" },
];
export default function App() {
return (
<SelectSearch<User>
options={users}
optionLabel="name"
optionValue="id"
onChange={(value) => console.log("Seleccionado:", value)}
placeholder="Buscar usuario..."
noResultsText="Sin resultados"
/>
);
}💡 Nota: El tipo
Useres solo un ejemplo. El componenteSelectSearches genérico (<T>) y funciona con cualquier estructura de datos.
Props
| Prop | Tipo | Requerido | Descripción |
|------|------|-----------|-------------|
| options | T[] | ✅ | Lista de opciones a mostrar. |
| optionLabel | keyof T | ✅ | Clave del objeto que se mostrará como texto en la lista. |
| optionLabel2 | keyof T | ❌ | Clave opcional del objeto que se mostrará como texto secundario (entre paréntesis). |
| optionValue | keyof T | ✅ | Clave del objeto que se usará como valor único de la opción. |
| value | T \| T[] | ❌ | Valor actual (single o multiple). |
| onChange | (value: T \| T[] \| null) => void | ✅ | Se ejecuta al seleccionar/deseleccionar opciones. |
| multiple | boolean | ❌ | Habilita selección múltiple. Default: false. |
| placeholder | string | ❌ | Texto del input de búsqueda. |
| ariaInputLabel | string | ❌ | Texto accesible para el input. Default: "Buscar opción". |
| ariaListboxLabel | string | ❌ | Texto accesible para el listbox. Default: "Opciones disponibles". |
| noResultsText | string | ❌ | Texto mostrado si no hay coincidencias. |
| className | string | ❌ | Clase CSS personalizada para el contenedor. |
Ejemplo con selección múltiple controlada (useState)
import { useState } from "react";
import { SelectSearch } from "accessible-search-select";
interface User {
id: number;
name: string;
}
const users: User[] = [
{ id: 1, name: "Juan" },
{ id: 2, name: "María" },
{ id: 3, name: "Pedro" },
];
export default function App() {
const [selectedUsers, setSelectedUsers] = useState<User[] | null>([]);
return (
<div>
<SelectSearch<User>
options={users}
optionLabel="name"
optionValue="id"
multiple
value={selectedUsers}
onChange={setSelectedUsers}
placeholder="Buscar..."
noResultsText="Nada encontrado"
/>
{selectedUsers && selectedUsers.length > 0 && (
<p>
Usuarios seleccionados:{" "}
{selectedUsers.map(u => u.name).join(", ")}
</p>
)}
</div>
);
}
Flujo de funcionamiento
La búsqueda actualiza searchTerm, se filtran las opciones y se renderiza la lista.
Al seleccionar, se llama onChange devolviendo un elemento único o un array según multiple.
Flujo de funcionamiento con ARIA
⚡ Accesibilidad:
El componente está diseñado para ser completamente accesible, compatible con lectores de pantalla y navegación con teclado.
- El input utiliza
aria-label(oariaInputLabel) para describir su función. - La lista utiliza
role="listbox"yaria-multiselectablesi es selección múltiple. - Cada elemento utiliza
role="option"yaria-selectedsegún su estado. - La navegación con teclado mantiene
aria-activedescendantapuntando al elemento resaltado (highlightedIndex), aunque no haya focus real en elli.
Personalización con CSS
Puedes pasar tu propia clase al componente usando la prop className para aplicar estilos externos a los elementos internos (input, ul, li).
import "accessible-search-select/style.css"; // Importa los estilos por defecto (opcional)
<SelectSearch<User>
className="mi-select-search"
options={users}
optionLabel="name"
optionValue="id"
onChange={(v) => console.log(v)}
placeholder="Buscar..."
/>
Clases internas
El componente utiliza las siguientes clases para aplicar estilos, siguiendo la convención BEM:
| Clase | Descripción |
|-------|------------|
| select-search__container | Contenedor principal del componente. |
| select-search__input | El input de búsqueda. |
| select-search__list | Lista (ul) que contiene los elementos filtrados. |
| select-search__item | Cada elemento (li) de la lista. |
| select-search__label2 | Texto secundario opcional (derivado de optionLabel2). Aparece entre paréntesis junto al label principal. |
| select-search__item--highlighted | Modificador visual para el elemento actualmente resaltado por navegación con teclado (no es focus real; se usa con aria-activedescendant). |
| select-search__item--selected | Modificador para un elemento seleccionado. |
| select-search-no-results | Mensaje que aparece cuando no hay resultados. |
💡 Nota: Las clases listadas arriba ya vienen preestilizadas en el CSS que exporta la librería, pero puedes sobrescribir sus estilos usando tu propia clase a través de la prop
className.
CSS externo (personalizable)
/* =============================
Contenedor principal (opcional)
============================= */
.mi-select-search {
/* Estilos para el contenedor principal */
}
/* =============================
Input de búsqueda
============================= */
.mi-select-search .select-search__input {
/* Sobrescribe estilos si lo deseas */
}
/* =============================
Lista de opciones
============================= */
.mi-select-search .select-search__list {
/* Sobrescribe estilos si lo deseas */
}
/* =============================
Elemento de lista
============================= */
.mi-select-search .select-search__item {
/* Estilos base para cada opción */
}
/* =============================
Segundo label opcional
============================= */
.mi-select-search .select-search__label2 {
/* Sobrescribe/resalta estilos si lo deseas */
}
/* =============================
Elemento actualmente resaltado
============================= */
.mi-select-search .select-search__item--highlighted {
/* Resalta el item seleccionado por teclado */
}
/* =============================
Elemento seleccionado
============================= */
.mi-select-search .select-search__item--selected {
/* Estilos para la opción seleccionada */
}
/* =============================
Mensaje de "sin resultados"
============================= */
.mi-select-search .select-search-no-results {
/* Estilos para el mensaje */
}
💡 Nota: Al pasar tu propia clase al componente mediante la prop
className, puedes sobrescribir los estilos de las clases internas (select-search__input,select-search__list,select-search__item, etc.) que ya vienen preestilizadas en el CSS de la librería.
Estructura interna

En la imagen puedes ver la estructura interna del componente: un input seguido por un ul con li para cada opción.
Licencia
MIT
