Skip to content

feat: add keyboard navigation support to the EntityActions component#3656

Merged
kasya merged 3 commits intoOWASP:mainfrom
Utkarsh-0304:feat/keyboard_navigation_support
Feb 3, 2026
Merged

feat: add keyboard navigation support to the EntityActions component#3656
kasya merged 3 commits intoOWASP:mainfrom
Utkarsh-0304:feat/keyboard_navigation_support

Conversation

@Utkarsh-0304
Copy link
Contributor

Proposed change

Resolves #3590

This PR adds keyboard navigation support to the EntityActions component in order to comply with WCAG 2.1 Level AA standards. Additionally, unit tests are added to verify keyboard navigation.

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 29, 2026

Summary by CodeRabbit

  • New Features

    • EntityActions dropdown now fully keyboard-accessible: open/close with keyboard, navigate items with Arrow keys, close with Escape, and activate with Enter/Space; focus is managed for predictable tab behavior.
  • Tests

    • Added comprehensive keyboard navigation tests for the EntityActions menu covering focus management, key bindings, activation behavior, wrapping, and edge cases.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds keyboard navigation and focus management to the EntityActions dropdown (Escape, ArrowUp/Down, Enter/Space), including trigger and menu-item refs, focusIndex state, outside-click handling, and an extensive Jest test suite validating keyboard interactions, focus behavior, activation, tabIndex management, and edge cases.

Changes

Cohort / File(s) Summary
Keyboard Navigation Implementation
frontend/src/components/EntityActions.tsx
Introduces focusIndex state, triggerButtonRef and menuItemsRef, outside-click handling, effect to focus indexed menu item, keyboard handler for Escape/ArrowUp/ArrowDown/Enter/Space, initialization/reset of focus on open/close, and tabIndex management on menu items.
Keyboard Navigation Tests
frontend/__tests__/unit/components/EntityActions.test.tsx
Adds comprehensive keyboard-focused tests: Escape closes menu and restores focus, initial focus on open, Arrow key navigation with wrap-around, Enter/Space activation and navigation/push behavior, tabIndex checks, robustness when menu closed, navigation to Edit route, status-change action handling, and event propagation edge cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • arkid15r
  • kasya
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely describes the main change: adding keyboard navigation support to the EntityActions component.
Description check ✅ Passed The PR description clearly relates to the changeset, explaining the purpose of adding keyboard navigation for WCAG compliance and mentioning the addition of unit tests.
Linked Issues check ✅ Passed The PR fully addresses issue #3590 by implementing keyboard navigation support in EntityActions and adding comprehensive unit tests to verify keyboard interactions for WCAG 2.1 Level AA compliance.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing keyboard navigation in EntityActions and adding related tests; no extraneous modifications outside the accessibility objective are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@frontend/__tests__/unit/components/EntityActions.test.tsx`:
- Around line 705-718: The test title is misleading: the spec inside the it
block for EntityActions uses editButton and asserts it has focus, so rename the
test description string (the it(...) first arg) to reflect that it validates
Enter when the first item is focused (e.g., "handles Enter key when first item
is focused" or similar) instead of "when no item is focused"; locate the it
block that references EntityActions, button/menu/editButton and mockPush and
update only the title text to match the actual assertions.
🧹 Nitpick comments (3)
frontend/src/components/EntityActions.tsx (2)

146-156: Consider adding aria-controls for complete WCAG compliance.

The trigger button has aria-expanded and aria-haspopup, but aria-controls is recommended for associating the button with the menu element it controls. This helps assistive technologies understand the relationship.

♿ Proposed enhancement
       <button
         ref={triggerButtonRef}
         type="button"
         onClick={handleToggle}
         className="cursor-pointer rounded px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700"
         aria-label={`${type === 'program' ? 'Program' : 'Module'} actions menu`}
         aria-expanded={dropdownOpen}
         aria-haspopup="true"
+        aria-controls={dropdownOpen ? 'entity-actions-menu' : undefined}
       >

Then add the id to the menu container:

       <div
         className="absolute right-0 z-20 mt-2 w-40 rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
         onKeyDown={handleKeyDown}
         role="menu"
         tabIndex={dropdownOpen ? 0 : -1}
+        id="entity-actions-menu"
       >

Based on learnings: "When implementing dropdown menus, always include proper accessibility features: ARIA attributes (aria-expanded, aria-haspopup, aria-controls)."


164-177: Consider clearing stale refs when options change.

The menuItemsRef array accumulates refs but is never trimmed. If options.length decreases (e.g., status change removes menu items), stale refs may remain at higher indices. While unlikely to cause issues given the current logic, clearing the array when options change would be more robust.

🧹 Proposed enhancement

Add a cleanup when options changes:

+ useEffect(() => {
+   menuItemsRef.current = []
+ }, [options.length])

Or inline in the map:

  {options.map((option, index) => {
+   if (index === 0) menuItemsRef.current.length = options.length
    // ...
  })}
frontend/__tests__/unit/components/EntityActions.test.tsx (1)

488-515: Keyboard navigation tests are partially orphaned from their describe block.

The describe('handles keyboard navigation', ...) block only contains two tests (Escape key behavior). The remaining keyboard navigation tests (lines 517-718) are defined outside this describe block at the top level of the main describe('EntityActions', ...). This makes the test organization inconsistent and harder to navigate.

🧪 Suggested fix: Move all keyboard tests inside the describe block
   describe('handles keyboard navigation', () => {
     it('closes menu when escape key is pressed', async () => {
       // ...
     })

     it('returns focus to trigger button when escape is pressed', async () => {
       // ...
     })
-  })
-
-  it('focuses first menu item when menu opens', () => {
+
+    it('focuses first menu item when menu opens', () => {
       // ...
+    })
+
+    it('navigates down through menu items with ArrowDown', async () => {
       // ...
+    })
+
+    // ... move all other keyboard tests here ...
+  })
 })

@sonarqubecloud
Copy link

@Utkarsh-0304 Utkarsh-0304 marked this pull request as ready for review January 29, 2026 13:36
@codecov
Copy link

codecov bot commented Feb 3, 2026

Codecov Report

❌ Patch coverage is 81.57895% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.55%. Comparing base (50115b0) to head (2133bd6).
⚠️ Report is 19 commits behind head on main.

Files with missing lines Patch % Lines
frontend/src/components/EntityActions.tsx 81.57% 2 Missing and 5 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3656      +/-   ##
==========================================
- Coverage   85.56%   85.55%   -0.02%     
==========================================
  Files         463      463              
  Lines       14306    14342      +36     
  Branches     1903     1912       +9     
==========================================
+ Hits        12241    12270      +29     
- Misses       1686     1688       +2     
- Partials      379      384       +5     
Flag Coverage Δ
backend 84.55% <ø> (ø)
frontend 88.31% <81.57%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
frontend/src/components/EntityActions.tsx 92.04% <81.57%> (-7.96%) ⬇️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 50115b0...2133bd6. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

Works great! Thanks @Utkarsh-0304 !

@kasya kasya enabled auto-merge February 3, 2026 03:39
@kasya kasya added this pull request to the merge queue Feb 3, 2026
Merged via the queue into OWASP:main with commit 15a44cc Feb 3, 2026
33 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add keyboard navigation support to meet WCAG accessibility standards

2 participants