Skip to content

fix(ui): sidebar arrow navigation#190

Merged
mpiton merged 3 commits intomainfrom
codex/issue-178-sidebar-arrow-navigation
Apr 10, 2026
Merged

fix(ui): sidebar arrow navigation#190
mpiton merged 3 commits intomainfrom
codex/issue-178-sidebar-arrow-navigation

Conversation

@mpiton
Copy link
Copy Markdown
Owner

@mpiton mpiton commented Apr 9, 2026

Summary

  • implement roving tabindex for sidebar navigation items
  • add ArrowUp/ArrowDown/Home/End keyboard handling without changing the active view on focus movement
  • cover the new focus behavior with sidebar and nav item tests

Why

Arrow key navigation in the sidebar did not move focus between items. Only Tab worked, which broke expected keyboard navigation for this control and left issue #178 unresolved.

Root Cause

Sidebar navigation items were plain buttons without shared focus state, per-item tabIndex management, or arrow-key handling.

Impact

Users can now move focus through the sidebar using arrow keys and jump to the first/last item with Home and End, matching the roving tabindex pattern expected for this kind of navigation.

Validation

  • npm test -- src/components/Sidebar/Sidebar.test.tsx src/components/Sidebar/NavItem.test.tsx
  • npm run build
  • npm run lint -- src/components/Sidebar/Sidebar.tsx src/components/Sidebar/NavItem.tsx src/components/Sidebar/Sidebar.test.tsx src/components/Sidebar/NavItem.test.tsx

Fixes #178


Summary by cubic

Adds roving tabindex and ArrowUp/ArrowDown/Home/End navigation to the sidebar so users can move focus between items without changing the active view. Groups nav items as "Primary views" for better screen reader context. Fixes #178.

  • Bug Fixes
    • Implemented roving tabindex with focusedView and refs; preserves focus when the view changes and resets to the active view if focus leaves the group.
    • Handled ArrowUp/ArrowDown/Home/End with wrap-around and preventDefault to avoid triggering navigation.
    • Exposed tabIndex, onFocus, onKeyDown, and buttonRef on NavItem; tests cover prop pass-through, roving behavior, and focus movement.

Written for commit c9c779a. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Keyboard navigation for the sidebar: Up/Down arrows, Home, End to move focus; only the active/focused item is tabbable and the nav is exposed as a primary views group.
  • Tests

    • Added tests validating sidebar roving tabindex, arrow/Home/End focus behavior, focus persistence when view changes, and per-item focus/key handling.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d3d59588-f2c1-4043-86ce-17e832123e02

📥 Commits

Reviewing files that changed from the base of the PR and between 08d34f9 and c9c779a.

📒 Files selected for processing (2)
  • src/components/Sidebar/Sidebar.test.tsx
  • src/components/Sidebar/Sidebar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Sidebar/Sidebar.test.tsx

📝 Walkthrough

Walkthrough

Added roving-tabindex keyboard navigation to the sidebar: Sidebar now tracks focused nav item, manages ArrowUp/ArrowDown/Home/End traversal and DOM focus; NavItem accepts tabIndex, onFocus, onKeyDown, and buttonRef to enable the behavior. Tests added for navigation and NavItem handlers.

Changes

Cohort / File(s) Summary
NavItem component & tests
src/components/Sidebar/NavItem.tsx, src/components/Sidebar/NavItem.test.tsx
Extended NavItemProps with tabIndex?: number, onFocus?: FocusEventHandler<HTMLButtonElement>, onKeyDown?: KeyboardEventHandler<HTMLButtonElement>, and buttonRef?: Ref<HTMLButtonElement>; props forwarded to the <button>. Added test verifying focus, keydown handler invocation, and tabindex behavior.
Sidebar navigation & tests
src/components/Sidebar/Sidebar.tsx, src/components/Sidebar/Sidebar.test.tsx
Implemented roving-tabindex: focusedView state, navGroupRef, navItemRefs[], focusNavItem helper, and handleNavKeyDown handling ArrowUp/ArrowDown/Home/End with cyclic moves. Updated ARIA attributes and added tests validating initial tabindex, arrow/home/end navigation, and focus retention on view change.

Sequence Diagram

sequenceDiagram
    participant User
    participant NavItem as NavItem Button
    participant Sidebar as Sidebar (Keyboard Handler)
    participant Store as Dashboard Store

    User->>NavItem: KeyDown (ArrowDown)
    NavItem->>Sidebar: onKeyDown(ArrowDown)
    activate Sidebar
    Sidebar->>Sidebar: compute next index
    Sidebar->>Store: set focusedView(next)
    Sidebar->>NavItem: refOf(next).focus()
    deactivate Sidebar

    NavItem->>Sidebar: onFocus(next)
    activate Sidebar
    Sidebar->>Store: sync focusedView
    Sidebar->>NavItem: set tabIndex=0 for focused, -1 for others
    deactivate Sidebar
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐰 I hop from key to key with nimble paws,
Arrow winds guide me through the cause.
Home to first, End to last I bound,
TabIndex tuned — focus all around. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(ui): sidebar arrow navigation' directly and concisely summarizes the main change: implementing arrow key navigation for the sidebar.
Linked Issues check ✅ Passed All coding requirements from #178 are met: roving tabindex pattern implemented (only focused item has tabindex='0'), ArrowUp/ArrowDown move focus between items, Home/End jump to first/last, and NavItem props extended with tabIndex, onFocus, onKeyDown, buttonRef.
Out of Scope Changes check ✅ Passed All changes are in scope: Sidebar.tsx and NavItem.tsx implement roving tabindex and keyboard handling per #178; test additions directly validate the new functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-178-sidebar-arrow-navigation

Comment @coderabbitai help to get the list of available commands and usage tips.

@mpiton mpiton changed the title [codex] fix(ui): sidebar arrow navigation fix(ui): sidebar arrow navigation Apr 9, 2026
@mpiton mpiton marked this pull request as ready for review April 9, 2026 16:13
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 9, 2026

Greptile Summary

This PR implements roving tabindex navigation for the sidebar by adding focusedView state, per-item buttonRef arrays, and a handleNavKeyDown handler that moves focus on ArrowUp/Down/Home/End without changing the active view. Both previously flagged concerns (the role="toolbar" annotation and the redundant setFocusedView before programmatic focus) have been resolved — the wrapper is now role="group" and focusNavItem only calls .focus().

Confidence Score: 5/5

Safe to merge — all previous P1 concerns have been resolved and no new issues were found.

Both prior findings (role=toolbar and the redundant setFocusedView call) are addressed. The roving tabindex logic, keyboard handler, and useEffect guard are all implemented correctly. No P0 or P1 issues remain.

No files require special attention.

Important Files Changed

Filename Overview
src/components/Sidebar/Sidebar.tsx Adds focusedView state, navItemRefs, handleNavKeyDown, and focusNavItem; previous issues with role and redundant setState are resolved; useEffect correctly guards against resetting focus while the nav group owns focus.
src/components/Sidebar/NavItem.tsx Clean extension of NavItemProps to expose tabIndex, onFocus, onKeyDown, and buttonRef; all props correctly forwarded to the underlying button element.
src/components/Sidebar/Sidebar.test.tsx Comprehensive tests covering roving tabindex initial state, ArrowUp/Down/Home/End navigation, wrap-around, and that the focused item is preserved when currentView changes externally while the nav group has focus.
src/components/Sidebar/NavItem.test.tsx Tests prop pass-through for tabIndex, onFocus, and onKeyDown; confirms new props do not affect existing rendering or click behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User presses key on NavItem] --> B{handleNavKeyDown}
    B -->|ArrowDown| C["nextIndex = index + 1 mod count"]
    B -->|ArrowUp| D["nextIndex = index - 1 + count mod count"]
    B -->|Home| E["nextIndex = 0"]
    B -->|End| F["nextIndex = count - 1"]
    B -->|Other key| G[return — no preventDefault]
    C & D & E & F --> H[event.preventDefault]
    H --> I[focusNavItem]
    I --> J["navItemRefs.current at index .focus()"]
    J --> K["onFocus fires: setFocusedView item.view"]
    K --> L["tabIndex: focusedView === item.view ? 0 : -1"]
    M[currentView changes externally] --> N{nav group owns activeElement?}
    N -->|Yes - user navigating| O[keep focusedView unchanged]
    N -->|No - focus elsewhere| P["setFocusedView currentView"]
Loading

Reviews (3): Last reviewed commit: "fix: address PR review comments" | Re-trigger Greptile

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Sidebar/Sidebar.tsx`:
- Around line 85-87: The current useEffect always calls
setFocusedView(currentView) which snaps the roving tabindex back even when a nav
button still has DOM focus; change the effect (useEffect) to only resync
focusedView to currentView when focus is outside the sidebar/nav group by
checking the container ref (e.g., navRef or sidebarRef) and using
navRef.current.contains(document.activeElement) (guarding for null/SSR) — call
setFocusedView(currentView) only if the container does NOT contain
document.activeElement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad5fba44-68f2-4444-9cd3-f71bf50a473d

📥 Commits

Reviewing files that changed from the base of the PR and between d3a1f8d and 08d34f9.

📒 Files selected for processing (1)
  • src/components/Sidebar/Sidebar.tsx

@mpiton mpiton merged commit abd75b9 into main Apr 10, 2026
6 of 7 checks passed
@mpiton mpiton deleted the codex/issue-178-sidebar-arrow-navigation branch April 10, 2026 13:38
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.

fix(ui): arrow key navigation does not move focus between sidebar items

1 participant