Skip to content

UI: the "Resource types" rail is implemented three different ways (Resources vs Search Parameters vs Search/Queries) #541

Description

@smunini

Summary

Every page labels its left-hand list "Resource types" (resources-types-heading, queries-rail-heading, sp-rail-types all resolve to the same English string), but there are three different implementations of that rail, with different markup, different interaction models, and different capabilities. The gap is most visible between Resources (/ui/resources) and Search Parameters (/ui/search-parameters).

Notably, crates/ui/templates/pages/search-parameters.html:31-34 describes its rail as "the primitive the Resources screen will share" — the Resources screen ended up not sharing it.

The three variants

Search Parameters Search / Saved Queries Resources
Template pages/search-parameters.html pages/search.html, pages/queries.html pages/resources.html
Placement aside.card.filter-rail inside content aside.card.filter-rail inside content {% block nav_panel %}.nav-panel, flush against the sidebar
Item markup <a class="filter-rail__item" href=…> <button class="filter-rail__item" data-rail-type> <button class="nav-panel__item" data-rail-type>
Selected state aria-current="true" (server) none .nav-panel__item--on (client, JS-toggled)
Type filter box server-side: htmx GET with q, hidden fields round-trip other filters client-side show/hide (saved-queries.js:1268) client-side show/hide (same code)
Counts server-rendered hydrated client-side hydrated client-side
Count meaning # of SearchParameters with that base type # of stored resource instances # of stored resource instances
"All types" row ✅ (rail_all)
"Recently used" group ✅ (resource-filter.js, localStorage hfs-recent-types)
URL reflects selection ✅ (linkable / bookmarkable / back-button)
Works without JS ✅ fully server-rendered ❌ rail is inert ❌ rail is inert
Type list source base types present in the SearchParameter registry (search_params.rs:410-433) compartments.resource_type_names(tenant, version) compartments.resource_type_names(tenant, version)

Why it matters

  • Same label, same visual slot, three different behaviors — a user who learns the rail on one page has to relearn it on the next.
  • Selecting a type on Resources doesn't change the URL, so the view can't be linked, bookmarked, or reached with the back button; on Search Parameters it can.
  • Resources/Search/Queries counts render blank until N client-side /{Type}?_summary=count&_total=accurate fetches land (4-at-a-time, ~145 types), and stay blank on failure. Search Parameters counts are always present.
  • The "Recently used" affordance and the "All types" escape hatch exist only on the page where they matter least.
  • The two count columns look identical but mean different things (search-parameter definitions vs. stored instances), with nothing in the UI distinguishing them.

Suggested direction (needs a decision)

Pick one canonical rail component and have all four pages use it. Open questions:

  1. Which placement wins — the sidebar-flush nav-panel (Resources, per the Figma "Resources" frame 142:2) or the in-content filter-rail card (Search Parameters, Search, Queries)?
  2. Server-driven links vs. client-driven buttons. The Search Parameters rail is a plain filtered list view and works without JS; the Resources rail drives an in-page search that never navigates. Reconciling these probably means the shared component takes real hrefs and progressively enhances.
  3. Counts: keep both meanings but label/annotate them, and decide whether the instance counts should be server-rendered rather than hydrated by ~145 client fetches.
  4. Should "All types" and "Recently used" be part of the shared primitive (i.e. show up on Resources too)?

Acceptance criteria

  • One shared "Resource types" rail component/partial used by Resources, Search, Saved Queries, and Search Parameters.
  • Consistent markup, selected state, and keyboard/screen-reader semantics across all four.
  • Documented, intentional decision on any behavior that stays page-specific (and why).
  • Rust + Playwright tests cover the shared component on at least Resources and Search Parameters.

Pointers

  • crates/ui/templates/pages/{resources,search,queries,search-parameters}.html
  • crates/ui/assets/saved-queries.js:1202-1280 (rail click, filter, count hydration)
  • crates/ui/assets/resource-filter.js (recents)
  • crates/ui/src/search_params.rs:410-445 (server-rendered rail + counts)
  • crates/ui/src/lib.rs:945-1015 (type list for Resources / Search / Queries)
  • crates/ui/assets/app.css:3602-3630 (.nav-panel__item), .filter-rail__*

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions