feat(ui): controlled-vocab inputs — combobox + multiselect + renames#198
Open
gerchowl wants to merge 5 commits into
Open
feat(ui): controlled-vocab inputs — combobox + multiselect + renames#198gerchowl wants to merge 5 commits into
gerchowl wants to merge 5 commits into
Conversation
Building blocks for controlled-vocabulary inputs (PR3): - ui/components/combobox.ts — fuzzy-match + create-new control for vendor / location. Operators pick an existing value (typing "digikey" surfaces the canonical "Digi-Key" via Fuse) or deliberately create a new one, flagged `isNew` so the caller can persist it. Accessible (role=combobox/listbox, aria-expanded/activedescendant, ↑/↓/Enter/Esc); closing commits typed text. - ui/components/tags-input.ts — multiselect chips for the components field (existing / staged-for-mint IDs instead of ";"-separated text). Storage stays ";"-joined; formatTag prettifies the chip while the value stays canonical. Backspace removes the last chip. - Contract label renames (SSoT → propagates to every table header + form): "Part number" → "Part #", "Manufacturer ID" → "External ID". 17 new unit tests (9 combobox + 8 tags-input). 273 unit green; build clean. Wiring into the Bind cells + vocab write-back lands next.
Wire the PR3 controlled-vocab components into the Bind queue cells: - vendor / location → makeCombobox: fuzzy pick-or-create. Suggestions come from the new registry/vocab.ts store (data-repo vocab ∪ contract seeds ∪ registry values ∪ session-staged). A created value is staged via stageVocabValue for write-back to the data-repo vocabulary. - components → makeTagsInput: chips picked from existing / staged-for-mint part IDs (componentCandidates), displayed grouped via fmtId, stored ";"-joined per the contract. - renderBindRow now receives ctx (for vocab); showFieldErrors widened to HTMLElement so the tags wrapper can carry validation state. New registry/vocab.ts (sourcing + session staging; data-repo JSON fetch + submit write-back land next). 4 vocab unit tests + a Bind e2e asserting fuzzy-surfaces-canonical, create-new, and chip-add. 281 unit + 77 e2e green.
Persist the controlled vocabularies as structured JSON in the data repo so
additions are reviewable and entries are expandable:
- registry/vocab-files.ts: vocabularies/{vendors,locations}.json shape +
pure parse/merge/serialize helpers. loadVocabularies() enriches combobox
suggestions on startup (non-blocking; degrades to contract seeds + registry
values if the files are absent — a 404 is expected pre-seed).
- submit.ts: writeBackVocab() merges session-created values into the data-repo
JSON and commits them onto the same proposal branch, so a brand-new
vendor/location shows up in the review PR diff. Best-effort — a vocab hiccup
never fails a submit whose registry.csv already committed; staged values
clear on success.
JSON (not flat text, per the design decision) so an entry can later carry
{ url, aliases, parent, ... } without a format change.
smoke.spec: optional-resource 404s (the absent vocab files) are filtered from
the boot-error assertion — they're handled by app logic, not JS errors, and
the pageerror handler still catches real exceptions.
9 vocab-files unit tests (parse/merge/serialize). 285 unit + 77 e2e green.
"Edit as a popup, like Lookup" — a pencil button on each Bind row opens a
roomy vertical form (the same combobox/tags controls as the inline cells)
instead of editing across the scrolling 11-column table.
- ui/components/row-editor.ts: openRowEditor({title, fields, values, ctx,
onSave}) over a shared modal. vendor/location → combobox, components →
tags-input, the rest → plain inputs. Operates on a flat values map; the
caller persists on Save. metadata/typeFields stay on the inline Properties
sub-row (fields = the json-excluded editable set).
- Bind rows gain a pencil action that seeds the editor from the queue item
and writes all fields back on Save.
- SSoT fix: the base .modal-card was only `position:relative` (every feature
modal hand-rolled its own card), so a new modal rendered transparent. Gave
.modal-card a default card appearance (bg/border/padding/shadow/max-height);
.detail-modal/.recovery-dialog keep their own specifics.
5 row-editor unit tests + a Bind e2e (open → edit → Save persists, reopen
confirms). 290 unit + 78 e2e green; popup verified clean via screenshot.
Give part-registry a distinctive aesthetic (replacing generic DaisyUI defaults), executed on the new theme substrate: - Self-hosted fonts (bundled → offline-safe PWA): Saira Semi Condensed for the wordmark/headers, Saira (variable) for UI, IBM Plex Mono for IDs and readouts. No external CDN. - DaisyUI themes reworked: "dark" = coal front-panel (#16181C) with a single signal-amber accent (#FFC23C) — the default + hero; "light" = a warm-paper variant for the toggle. Sharp 2-3px radii (machined), hairline borders. - Instrument detailing in style.css: faint grid + top amber wash + film-grain overlay; a measurement-tick ruler under the header; an amber pulse-dot on the wordmark; tracked-uppercase display headers; monospace IDs; status rendered as glowing LEDs (amber=bound, dim=unbound, red=void); kind badges in the same LED language; recessed inputs with an amber focus ring; modals as lifted faceplates. - Motion: a staggered power-on boot reveal (header → nav → body), a subtle per-tab redraw, amber engaged-underline on the active tab, signal-amber row hover (inset edge). All gated behind prefers-reduced-motion. 290 unit + 78 e2e green (fonts bundled, no new network). Verified across Lookup/Bind/Print + the popup editor via screenshots.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #197. Controlled-vocabulary inputs for data quality + a popup row editor.
What
ui/components/combobox.ts) for vendor / location: typing "digikey" surfaces the canonical "Digi-Key" (Fuse) so spellings converge; or deliberately create a new value. Accessible (role=combobox/listbox, ↑/↓/Enter/Esc).ui/components/tags-input.ts) for components: chips picked from existing / staged-for-mint part IDs instead of;-typed text. Storage stays;-joined per the contract.registry/vocab.ts+vocab-files.ts): suggestions = data-repovocabularies/*.json∪ contract seeds ∪ registry values ∪ session-staged. New values are written back to the data-repo JSON on submit (committed onto the same proposal branch — the addition shows up in the review PR diff). JSON so entries are expandable later. Best-effort: a vocab hiccup never fails a submit.Part number→ Part #,Manufacturer ID→ External ID.ui/components/row-editor.ts): a pencil per Bind row opens a roomy modal form (same controls) — easier than editing across the scrolling table. Also fixed the base.modal-cardto be a real card (it wasposition:relativeonly).Verification
290 unit + 78 e2e green. New: 36 unit tests (combobox 9, tags 8, vocab 4, vocab-files 9, row-editor 5, +data-table 7 from #197 base) + 3 Bind e2e (fuzzy/create, tags, popup-edit-persists). Combobox + popup verified live via screenshots.
Note
The data-repo
vocabularies/*.jsonare absent until first written, so the read path logs two benign 404s (handled gracefully; filtered in the smoke test). Seedingvendors.json/locations.jsonin the sandbox removes them.🤖 Generated with Claude Code