Skip to content

Conversation

ManiBAJPAI22
Copy link

@ManiBAJPAI22 ManiBAJPAI22 commented Aug 23, 2025

Background

Adds accessible keyboard navigation support for the court search results in the Courts page search bar.

New Features

  • ArrowUp/ArrowDown: Navigate through search results with visual feedback
  • Enter Key: Select the highlighted result and navigate to that court
  • Visual Highlighting: Selected items get blue left border and background styling
  • Smart Selection: Current court only shows as selected when no keyboard navigation is active

Technical Changes

  • Added selectedIndex state to track keyboard navigation
  • Implemented handleKeyDown for ArrowUp/ArrowDown/Enter key handling
  • Updated StyledCard component to support keyboardSelected prop
  • Added proper event prevention and boundary checking
  • Maintained all existing mouse navigation functionality

Testing

  • ✅ ArrowUp/ArrowDown navigation works correctly
  • ✅ Enter key selects and navigates to highlighted results
  • ✅ Visual highlighting appears for keyboard-selected items
  • ✅ Mouse clicks continue to work as before
  • ✅ Selection state resets properly on search changes

Files Changed

  • web/src/pages/Courts/CourtDetails/TopSearch.tsx - Added keyboard navigation logic and styling

Accessibility Impact

This change significantly improves keyboard accessibility for users who rely on keyboard navigation, making the court search functionality more inclusive and user-friendly.

🎯 What does this PR do?

Adds accessible keyboard navigation support for the court search results in the Courts page search bar.

New Features

  • ArrowUp/ArrowDown: Navigate through search results with visual feedback
  • Enter Key: Select the highlighted result and navigate to that court
  • Visual Highlighting: Selected items get blue left border and background styling
  • Smart Selection: Current court only shows as selected when no keyboard navigation is active

🔧 Technical Changes

  • Added selectedIndex state to track keyboard navigation
  • Implemented handleKeyDown for ArrowUp/ArrowDown/Enter key handling
  • Updated StyledCard component to support keyboardSelected prop
  • Added proper event prevention and boundary checking
  • Maintained all existing mouse navigation functionality

🧪 Testing

  • ✅ ArrowUp/ArrowDown navigation works correctly
  • ✅ Enter key selects and navigates to highlighted results
  • ✅ Visual highlighting appears for keyboard-selected items
  • ✅ Mouse clicks continue to work as before
  • ✅ Selection state resets properly on search changes

📁 Files Changed

  • web/src/pages/Courts/CourtDetails/TopSearch.tsx - Added keyboard navigation logic and styling

Accessibility Impact

This change significantly improves keyboard accessibility for users who rely on keyboard navigation, making the court search functionality more inclusive and user-friendly.

image

PR-Codex overview

This PR enhances the TopSearch component by adding keyboard navigation support, allowing users to navigate search results using arrow keys. It also modifies the styling of StyledCard to accommodate a new keyboardSelected prop for visual feedback.

Detailed summary

  • Updated StyledCard to accept a new prop keyboardSelected.
  • Adjusted padding, border, and background color styles based on keyboardSelected.
  • Added a selectedIndex state to track the currently selected search result.
  • Implemented handleKeyDown for keyboard navigation (ArrowDown, ArrowUp, Enter).
  • Created handleSearchChange to update search input and reset selection.
  • Modified handleCourtClick to navigate and reset search on court selection.
  • Updated the mapping of filteredCourts to apply keyboardSelected logic.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Added keyboard navigation to top search results: use Arrow Up/Down to move through results and Enter to open the selected court; selection resets after navigation.
    • Clicking a result also opens the court and clears the search.
  • Style

    • Improved visual highlighting for keyboard-selected results with clearer padding, border, and background while preserving current-court highlighting when no keyboard selection is active.

@ManiBAJPAI22 ManiBAJPAI22 requested a review from a team as a code owner August 23, 2025 01:33
Copy link

netlify bot commented Aug 23, 2025

Deploy Preview for kleros-v2-testnet ready!

Name Link
🔨 Latest commit d38360e
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-testnet/deploys/68b17d366bd65c0008530d00
😎 Deploy Preview https://deploy-preview-2106--kleros-v2-testnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link

netlify bot commented Aug 23, 2025

👷 Deploy request for kleros-v2-testnet-devtools pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit d38360e

Copy link

netlify bot commented Aug 23, 2025

‼️ Deploy request for kleros-v2-neo rejected.

Name Link
🔨 Latest commit 05b97b3

Copy link

netlify bot commented Aug 23, 2025

👷 Deploy request for kleros-v2-university pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit d38360e

Copy link
Contributor

coderabbitai bot commented Aug 23, 2025

Walkthrough

Adds keyboard navigation to TopSearch: tracks a selectedIndex, handles ArrowUp/ArrowDown/Enter, updates StyledCard to accept a keyboardSelected prop, and refactors input/row handlers to manage navigation, selection highlighting, and navigation to a court.

Changes

Cohort / File(s) Summary
TopSearch keyboard navigation
web/src/pages/Courts/CourtDetails/TopSearch.tsx
Added selectedIndex state and handleKeyDown to handle ArrowUp/ArrowDown/Enter; added handleSearchChange and handleCourtClick; pass keyboardSelected to StyledCard; extended StyledCard props to { selected, keyboardSelected }; added typing to flattenCourts(court: any, parent: any = null); updated rendering to use index-based highlighting and combined selection logic.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant TS as TopSearch
  participant R as Router

  U->>TS: Type in search input
  TS->>TS: handleSearchChange()\nupdate query, reset selectedIndex, filter results

  U->>TS: Press ArrowDown / ArrowUp
  TS->>TS: handleKeyDown()\nupdate selectedIndex -> mark keyboardSelected

  U->>TS: Press Enter
  TS->>TS: handleKeyDown(Enter)\nresolve selected court
  TS->>R: Navigate to court
  TS->>TS: Clear search & reset selection
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Suggested labels

Type: Feature🗿, Package: Web

Suggested reviewers

  • alcercu

Poem

I hop through lists with nimble feet,
Down, up, I choose the court to meet.
Press Enter—off I bound and go,
Clearing crumbs of search below.
A rabbit’s tap, a search well done. 🐰✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 05b97b3 and d38360e.

📒 Files selected for processing (1)
  • web/src/pages/Courts/CourtDetails/TopSearch.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/pages/Courts/CourtDetails/TopSearch.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/pages/Courts/CourtDetails/TopSearch.tsx (1)

171-178: Add ARIA combobox semantics (combobox + listbox/option + activedescendant).

The feature is “accessible” only if SRs can perceive the active option. Add proper roles/attrs to the input, list, and options, and wire aria-activedescendant to the active option id.

Apply:

             <StyledSearchbar
               dir="auto"
               type="text"
               placeholder="Search"
               value={search}
-              onChange={handleSearchChange}
-              onKeyDown={handleKeyDown}
+              onChange={handleSearchChange}
+              onKeyDown={handleKeyDown}
+              role="combobox"
+              aria-autocomplete="list"
+              aria-expanded={Boolean(search && filteredCourts.length)}
+              aria-controls={listboxId}
+              aria-activedescendant={activeOptionId ?? undefined}
             />
-            {search && filteredCourts.length > 0 && (
-              <SearchResultsContainer>
+            {search && filteredCourts.length > 0 && (
+              <SearchResultsContainer id={listboxId} role="listbox">
                 {filteredCourts.map((court, index) => (
                   <StyledCard
                     key={court.id}
                     selected={selectedIndex === -1 && court.id === currentCourtId}
-                    keyboardSelected={index === selectedIndex}
+                    $keyboardSelected={index === selectedIndex}
+                    role="option"
+                    aria-selected={index === selectedIndex}
+                    id={`court-option-${court.id}`}
                     onClick={() => handleCourtClick(court.id)}
                   >

Add these helper vars near the top of the component (before return):

const listboxId = "court-search-results";
const activeOptionId =
  selectedIndex >= 0 ? `court-option-${filteredCourts[selectedIndex].id}` : undefined;

This keeps focus on the input while exposing the active descendant to AT.

Also applies to: 179-181, 181-187

🧹 Nitpick comments (4)
web/src/pages/Courts/CourtDetails/TopSearch.tsx (4)

75-86: Stop prop leakage: make keyboardSelected transient ($keyboardSelected).

Styled-components will forward unknown props down the tree unless marked transient. Keep using selected (the base Card might rely on it), but switch keyboardSelected to a transient prop to avoid non-standard DOM attributes like keyboardselected="true".

Apply:

-const StyledCard = styled(Card)<{ selected: boolean; keyboardSelected: boolean }>`
+const StyledCard = styled(Card)<{ selected: boolean; $keyboardSelected: boolean }>`
   ${hoverShortTransitionTiming}
   height: auto;
   width: 100%;
-  padding: ${({ selected, keyboardSelected }) => (selected || keyboardSelected ? "16px 13px" : "16px")};
+  padding: ${({ selected, $keyboardSelected }) => (selected || $keyboardSelected ? "16px 13px" : "16px")};
   cursor: pointer;
   border: none;
-  border-left: ${({ selected, keyboardSelected, theme }) =>
-    selected || keyboardSelected ? `3px solid ${theme.primaryBlue}` : "none"};
-  background-color: ${({ selected, keyboardSelected, theme }) =>
-    selected || keyboardSelected ? theme.mediumBlue : "transparent"};
+  border-left: ${({ selected, $keyboardSelected, theme }) =>
+    selected || $keyboardSelected ? `3px solid ${theme.primaryBlue}` : "none"};
+  background-color: ${({ selected, $keyboardSelected, theme }) =>
+    selected || $keyboardSelected ? theme.mediumBlue : "transparent"};

And update usage:

-                    keyboardSelected={index === selectedIndex}
+                    $keyboardSelected={index === selectedIndex}

Also applies to: 185-186


119-127: Ensure ID comparison is type-safe to keep “smart selection” working.

Route params are strings; court IDs may be numeric or string. Coerce to string on both sides to avoid silently breaking the “current court” highlighting.

-    const selectedCourt = courts.find((c) => c.id === currentCourtId);
+    const selectedCourt = courts.find((c) => String(c.id) === String(currentCourtId));

And in render:

-                    selected={selectedIndex === -1 && court.id === currentCourtId}
+                    selected={selectedIndex === -1 && String(court.id) === String(currentCourtId)}

Optional micro-optimizations:

  • Precompute the flattened courts once per data change to avoid re-flattening on every keystroke:
    const flattenedCourts = useMemo(
      () => (data?.court ? flattenCourts(data.court) : []),
      [data?.court]
    );
    const filteredCourts = useMemo(() => {
      const q = search.toLowerCase();
      const courts = flattenedCourts.filter((c) => c.name.toLowerCase().includes(q));
      ...
    }, [flattenedCourts, search, currentCourtId]);
    

Also applies to: 184-184


128-149: Handle Enter/Escape consistently; prevent default on Enter.

Prevent the default Enter behavior and add Escape to clear/close results. This avoids stray “submit/beep” behavior and lets users exit navigation quickly.

   switch (e.key) {
     case "ArrowDown":
       e.preventDefault();
       setSelectedIndex((prev) => Math.min(prev + 1, filteredCourts.length - 1));
       break;
     case "ArrowUp":
       e.preventDefault();
       setSelectedIndex((prev) => Math.max(prev - 1, -1));
       break;
     case "Enter":
-        if (selectedIndex >= 0) {
+        e.preventDefault();
+        if (selectedIndex >= 0) {
           navigate(`/courts/${filteredCourts[selectedIndex].id}`);
           setSearch("");
           setSelectedIndex(-1);
-        }
-        break;
+        }
+        break;
+    case "Escape":
+        if (search) {
+          e.preventDefault();
+          setSearch("");
+          setSelectedIndex(-1);
+        }
+        break;
   }

Nice-to-have: support Home/End to jump to first/last result.


128-149: Keep the highlighted row in view while arrowing.

As the user navigates, the active row can scroll out of view. Consider scrolling it into view when selectedIndex changes.

Example:

// inside component
const listRef = React.useRef<HTMLDivElement | null>(null);

// attach to <SearchResultsContainer ref={listRef} ... />

React.useEffect(() => {
  if (selectedIndex < 0) return;
  const el = document.getElementById(`court-option-${filteredCourts[selectedIndex]?.id}`);
  el?.scrollIntoView({ block: "nearest" });
}, [selectedIndex, filteredCourts]);

Also applies to: 181-187

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f02d718 and 05b97b3.

📒 Files selected for processing (1)
  • web/src/pages/Courts/CourtDetails/TopSearch.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
web/src/pages/Courts/CourtDetails/TopSearch.tsx (1)
web/src/styles/commonStyles.ts (1)
  • hoverShortTransitionTiming (3-5)
🔇 Additional comments (1)
web/src/pages/Courts/CourtDetails/TopSearch.tsx (1)

150-154: Reset selection on search change and on click — LGTM.

State resets are correct and avoid stale/highlighted rows after actions.

Also applies to: 155-159

coderabbitai[bot]
coderabbitai bot previously approved these changes Aug 23, 2025
@ManiBAJPAI22
Copy link
Author

@tractorss I have tried to fix the issue can you please review it.

The problem was in the ArrowUp key handler where the modulo arithmetic could result in selectedIndex becoming -1, causing the navigation bug where users needed to press keys twice to move properly.

Copy link
Contributor

@tractorss tractorss left a comment

Choose a reason for hiding this comment

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

lgtm 🚀

<StyledCard
key={court.id}
selected={court.id === currentCourtId}
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for your contribution @ManiBAJPAI22 !

I would like to keep the original logic to highlight the selected court, and make it so such that the keyboardSelected style doesn't clash with it (i.e. no primary-blue left-border). Maybe some kind of outline? also, we have a new version of the ui-components library that will eventually substitute all these components, and this new library has functionality already built-in, although we don't have a date for the migration yet.

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.

3 participants