feat(fase-11): TagChip + TagPicker components + filter unit tests (11.3.1-2)

TagChip — small pill rendering a tag's name with the colour from the 8-preset
palette. Three usage shapes (mutually exclusive):
  * display-only (default)
  * with onRemove → renders an inline X (used by picker-selected chips)
  * with onSelect → renders the whole chip as a button (used by the lateral
    filter bar and by item-row chips that filter the list on click)
Nested <button> was the SSR warning from the first cut — fixed by making
onSelect / onRemove mutually exclusive.

TagPicker — combobox-style picker driven by filterTagOptions (pure helper
in tag-picker-filter.ts so it can be unit-tested without rendering). Lists
matches by substring, surfaces a "Create `{query}`" affordance only when no
existing tag matches the trimmed query exactly. Enter key prefers toggling
the first match, falls back to creating. Re-usable from the item modal,
the list-filter bar, and the settings tag-management section.

tag-picker-filter.test.ts — 7 unit tests (TP-01..07) covering case-
insensitive substring match, empty query → full list, create affordance
gating on exact-match absence, exclusion of already-selected ids, and
createName preserving verbatim user input (no lowercasing).

tailwind.config.ts safelists the runtime-built bg-/text-/ring-/dot
combinations for all 8 preset colours so JIT doesn't strip them.

Messages: 9 new keys in en + es (tag picker, filter bar, settings section).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 03:08:54 +02:00
parent c1152fac90
commit 8fa629ad74
7 changed files with 338 additions and 2 deletions

View File

@@ -220,5 +220,15 @@
"manage_dissolve_confirm_button": "Disolver permanentemente",
"sidebar_create_collective": "+ Nuevo colectivo",
"create_collective_modal_title": "Nuevo colectivo",
"create_collective_modal_button": "Crear"
"create_collective_modal_button": "Crear",
"tag_picker_placeholder": "Buscar o crear etiqueta",
"tag_picker_create": "Crear «{name}»",
"tag_picker_empty": "Aún no hay etiquetas.",
"tag_picker_label": "Etiquetas",
"list_filter_by_tag": "Filtrar por etiqueta",
"list_filter_clear": "Limpiar",
"settings_tags_section": "Etiquetas",
"settings_tags_empty": "Aún no hay etiquetas — créalas desde cualquier lista.",
"settings_tags_delete": "Eliminar",
"settings_tags_color": "Color"
}