Skip to content

feat(:sidebar_item): add avatar_url + state props (Wave 3.7 Class C extension, DRAFT, Pascal-review REQUIRED)#135

Draft
ty13r wants to merge 1 commit into
mainfrom
claude/extension-sidebar-item-avatar-state-wave-3-7
Draft

feat(:sidebar_item): add avatar_url + state props (Wave 3.7 Class C extension, DRAFT, Pascal-review REQUIRED)#135
ty13r wants to merge 1 commit into
mainfrom
claude/extension-sidebar-item-avatar-state-wave-3-7

Conversation

@ty13r
Copy link
Copy Markdown
Member

@ty13r ty13r commented May 18, 2026

Summary

Extends the canonical :sidebar_item widget with two new optional props per unified_ui issue #184 (Wave AshUI-3.7 comp-fidelity audit findings SB-4 + SB-5).

  • avatar_url (String.t() | nil, default nil): when set, renders a small <img aria-hidden="true"> before the item label. Used for DM rows in the sidebar showing the target's avatar beside the name.
  • item_state (:default | :stalled | :blocked | :errored | nil, default nil): applies a BEM modifier class live-ui-sidebar-item--state-{value} and a data-live-ui-item-state hook attribute for CSS/JS targeting. Semantic workflow-state meaning for stalled/blocked/errored items.

Both are backward-compatible additive changes (nil default preserves all existing behavior).

Files modified

  • packages/unified_iur/lib/unified_iur/widgets/components.ex — Stage 1: sidebar_item/3 constructor accepts avatar_url and item_state opts; validates item_state against [:default, :stalled, :blocked, :errored]; uses maybe_put so nil values are omitted
  • packages/live_ui/lib/live_ui/renderer.ex — Stage 3: replaces the inline :sidebar_item HEEx template with a dispatch to LiveUi.Widgets.SidebarItem.component (same pattern as :thread_card)
  • packages/live_ui/lib/live_ui/widgets/sidebar_item.ex — Stage 4: new LiveUi.Widgets.SidebarItem Phoenix.Component with full attr declarations + render/1
  • packages/unified_iur/test/unified_iur/widgets/components_test.exs — 10 new describe "sidebar_item constructor" tests covering defaults, avatar_url present/absent, all valid item_state values, invalid state raises, backward-compat, and combined use
  • packages/live_ui/test/live_ui/sidebar_item_widget_test.exs — 16 new Stage-4 tests: true-widget attr, label render, aria-current + BEM selected, avatar img present/absent, state hook + modifier for each state, nil/default state omission, renderer dispatch

Test results

  • packages/unified_iur: 34 tests, 0 failures (components_test.exs only; 7 pre-existing failures in other test files are unrelated to this PR)
  • packages/live_ui: 587 tests, 0 failures (includes 16 new sidebar_item tests)

Open questions for Pascal (verbatim from unified_ui #184)

  1. Avatar composition: currently renders <img> inline directly (smallest delta). Should :sidebar_item instead compose with the canonical :avatar widget (ash_ui PR feat(live_ui): add Avatar widget Phoenix.Component (Wave 3.9.1a Stage-4) #116, DRAFT)? Composing is cleaner separation but a larger PR. Inline <img> used here pending Pascal decision.
  2. State enum completeness: :stalled | :blocked | :errored are the audit-required values. Are :loading or :in_progress needed? Should it overlap with the :loading state convention from button widgets?
  3. state vs tone naming: item_state was chosen to avoid collision with LiveUi.Component's common :state assign (style hook). Does this widget align with the :tone convention used elsewhere? Or is state more appropriate here for workflow-state semantics?
  4. ARIA for :blocked: comp shows visual treatment (dimmed + lock icon) but items remain clickable. Should aria-disabled="true" be added for :blocked? Or just a state CSS class with no ARIA implication?
  5. Avatar fallback: when avatar_url not provided but the item is a DM row, should there be an initials fallback? Left as consumer-side composition for now.

Cross-references

Status

DRAFT — Pascal review required before merge. Standing auto-merge does NOT apply.

🤖 Generated with Claude Code

…xtension)

Extends the canonical :sidebar_item widget with two new optional attrs per
unified_ui issue #184 (audit findings SB-4 and SB-5):

- avatar_url: String.t() | nil -- renders <img aria-hidden="true"> before the
  label when set; used for DM row avatars in the sidebar.
- item_state: :default | :stalled | :blocked | :errored | nil -- applies
  a BEM modifier class (live-ui-sidebar-item--state-{value}) and a
  data-live-ui-item-state hook attribute for CSS/JS targeting.

Both are backward-compatible additive changes (default nil = existing behavior).

Stage 1 (unified_iur): sidebar_item/3 constructor gains avatar_url + item_state
  opts; validates item_state against enum; maybe_put omits nils.
Stage 3 (live_ui renderer): :sidebar_item clause updated to dispatch to the
  new LiveUi.Widgets.SidebarItem.component (same pattern as :thread_card).
Stage 4 (live_ui): new LiveUi.Widgets.SidebarItem Phoenix.Component module.
Tests: 10 new unified_iur constructor tests + 16 new Stage-4 live_ui tests.

Open questions for Pascal (verbatim from unified_ui #184):
1. Avatar composition: inline <img> vs composing :avatar widget (ash_ui #116)?
2. State enum completeness: :loading, :in_progress deferred pending confirmation.
3. state vs tone naming: item_state chosen to avoid clash with LiveUi.Component
   common :state assign; Pascal may prefer another name.
4. ARIA for :blocked: aria-disabled="true" deferred; comp shows visual treatment
   but clickability intent unclear.
5. Avatar fallback: initials fallback for missing avatar_url is consumer-side.

DRAFT PR -- Pascal review required before merge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant