feat: Implemented autocomplete dropdown for GitHub organization search with chip support#87
feat: Implemented autocomplete dropdown for GitHub organization search with chip support#87Ridima28 wants to merge 4 commits into
Conversation
…state in home to control the suggestion display
WalkthroughA new ChangesOrg Autocomplete Search Dropdown
Sequence Diagram(s)sequenceDiagram
participant User
participant HomePage
participant searchOrgs
participant GitHubSearchAPI
rect rgba(100, 149, 237, 0.5)
Note over User,HomePage: Typing triggers debounce
User->>HomePage: types in search input
HomePage->>HomePage: debounce 300ms
HomePage->>searchOrgs: searchOrgs(input.trim())
searchOrgs->>GitHubSearchAPI: GET /search/users?q=...&type=org
GitHubSearchAPI-->>searchOrgs: { items: [...] }
searchOrgs-->>HomePage: items array (max 6)
HomePage->>User: renders dropdown with org.login suggestions
end
rect rgba(144, 238, 144, 0.5)
Note over User,HomePage: Selecting a suggestion
User->>HomePage: mousedown on suggestion
HomePage->>HomePage: selectSuggestion — dedup, add to chips
HomePage->>User: clears input, hides dropdown
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/HomePage.jsx`:
- Around line 18-29: The useEffect hook in HomePage.jsx has a race condition
where multiple searchOrgs calls can complete out of order, showing stale
results, and lacks error handling if the search fails. Add a cancelled flag
within the useEffect to track whether the current debounced request is still
valid, then before updating state with setSuggestion and setShowSuggestions,
check that the flag is false to prevent stale results from overwriting newer
ones. Additionally, wrap the searchOrgs call in a try-catch block to handle
potential errors (network failures, rate limits), and when an error occurs,
either clear suggestions or set an error state to provide user feedback instead
of silently failing. Make sure to set the cancelled flag to true in the cleanup
function before clearing the timeout to ensure cancelled requests don't update
state.
- Around line 109-111: The button element with the onClick handler for go() and
the "EXPLORE" label is missing an explicit type attribute, causing it to default
to type="submit" which can trigger unintended form submissions. Add
type="button" attribute to this button element. Additionally, locate and apply
the same fix to the other button elements referenced on lines 172 and 186 by
adding the type="button" attribute to each of those buttons as well.
- Around line 15-16: Fix the state variable naming inconsistencies in the
useState declarations by ensuring setter names match their corresponding state
variable names. Change the setter `setSuggestion` to `setSuggestions` to match
the state variable `suggestions`, and change the setter `setShowSuggestions` to
`setShowSuggestion` to match the state variable `showSuggestion`. Then update
all references throughout the HomePage component where these setters are called,
including calls to setSuggestion in the input change handlers and search result
handlers, as well as the reference to showSuggestion in any conditional
rendering logic.
- Around line 100-107: The input element has two issues that need fixing: first,
the onBlur handler that calls addChip(input) can fire before suggestion
selection handlers, causing duplicate chips to be added when users click on
suggestions. Fix this by adding a check for !showSuggestions in the onBlur
condition so the handler only runs when the suggestion dropdown is not visible.
Second, the input element is missing an aria-label attribute which is required
for accessibility and screen reader compatibility. Add an appropriate aria-label
to the input element that describes its purpose, such as indicating it's for
adding organization names.
- Around line 117-154: The autocomplete dropdown suggestions lack accessibility
and keyboard navigation support. First, add role="button" to the suggestion
items (the divs with onMouseDown handlers) and include hover styles for visual
feedback. Second, implement keyboard navigation by adding a useEffect hook that
listens for ArrowDown, ArrowUp, and Escape key events to navigate through
suggestions and close the dropdown. Third, externalize the hardcoded "org"
string label used in the suggestions list to an i18n resource file instead of
keeping it as a literal string in the JSX, following the path instructions for
internationalization compliance.
In `@src/services/github.js`:
- Around line 88-98: Refactor the searchOrgs function to use the existing
fetchWithCache infrastructure instead of making a direct fetch call, following
the same pattern as fetchOrg and fetchRepos functions. The searchOrgs function
currently bypasses authentication, caching, and rate limit handling. To fix
this, replace the direct fetch implementation with a call to fetchWithCache,
passing the GitHub search users API endpoint and the query parameter. You will
need to obtain the PAT token from the useApp() context hook and pass it to
fetchWithCache to enable proper authentication and access the higher rate
limits, along with the established caching and rate limit error handling
mechanisms.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: e42877ff-50ef-4207-ba92-6d01d792e6fa
📒 Files selected for processing (3)
README.mdsrc/pages/HomePage.jsxsrc/services/github.js
| useEffect(() => { | ||
| if (!input.trim()) { | ||
| setSuggestion([]) | ||
| return | ||
| } | ||
| const t = setTimeout(async () => { | ||
| const result = await searchOrgs(input.trim()) | ||
| setSuggestion(result.slice(0,6)) | ||
| setShowSuggestions(true) | ||
| }, 300) | ||
| return () => clearTimeout(t) | ||
| }, [input]) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Add race condition protection and error handling.
The debounced search has two issues:
-
Race condition: Multiple async
searchOrgscalls can complete out of order. If a user types "face" → "faceb" → "faceboo" quickly, all three API calls execute, and whichever completes last wins—potentially showing stale results for "face" after the user has typed "faceboo". -
Silent failures: If
searchOrgsfails (rate limit, network error), the user sees no feedback.
🔒 Proposed fix with cancellation and error handling
useEffect(() => {
if (!input.trim()) {
setSuggestion([])
+ setShowSuggestions(false)
return
}
+ let cancelled = false
const t = setTimeout(async () => {
- const result = await searchOrgs(input.trim())
- setSuggestion(result.slice(0,6))
- setShowSuggestions(true)
+ try {
+ const result = await searchOrgs(input.trim())
+ if (!cancelled) {
+ setSuggestion(result.slice(0,6))
+ setShowSuggestions(result.length > 0)
+ }
+ } catch (err) {
+ if (!cancelled) {
+ setSuggestion([])
+ setShowSuggestions(false)
+ }
+ }
}, 300)
- return () => clearTimeout(t)
+ return () => {
+ clearTimeout(t)
+ cancelled = true
+ }
}, [input])The cancelled flag ensures stale results from earlier searches don't overwrite newer ones.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/HomePage.jsx` around lines 18 - 29, The useEffect hook in
HomePage.jsx has a race condition where multiple searchOrgs calls can complete
out of order, showing stale results, and lacks error handling if the search
fails. Add a cancelled flag within the useEffect to track whether the current
debounced request is still valid, then before updating state with setSuggestion
and setShowSuggestions, check that the flag is false to prevent stale results
from overwriting newer ones. Additionally, wrap the searchOrgs call in a
try-catch block to handle potential errors (network failures, rate limits), and
when an error occurs, either clear suggestions or set an error state to provide
user feedback instead of silently failing. Make sure to set the cancelled flag
to true in the cleanup function before clearing the timeout to ensure cancelled
requests don't update state.
| {showSuggestion && suggestions.length > 0 && ( | ||
| <div style={{ | ||
| position: 'relative', | ||
| width: '100%', | ||
| }}> | ||
| <div style={{ | ||
| position: 'absolute', | ||
| top: 6, | ||
| left: 0, | ||
| right: 0, | ||
| background: 'var(--surface)', | ||
| border: '1px solid var(--border)', | ||
| borderRadius: 8, | ||
| zIndex: 20, | ||
| maxHeight: 220, | ||
| overflowY: 'auto' | ||
| }}> | ||
| {suggestions.map(org => ( | ||
| <div | ||
| key={org.login} | ||
| onMouseDown={() => selectSuggestion(org.login)} | ||
| style={{ | ||
| padding: '10px 12px', | ||
| cursor: 'pointer', | ||
| fontSize: 13, | ||
| display: 'flex', | ||
| justifyContent: 'space-between' | ||
| }} | ||
| > | ||
| <span>{org.login}</span> | ||
| <span style={{ color: 'var(--text3)', fontSize: 11 }}> | ||
| org | ||
| </span> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Enhance accessibility and keyboard navigation.
The autocomplete dropdown has several gaps:
- Semantic HTML: Clickable
<div>elements should userole="button"or be actual<button>elements for screen reader users. - Keyboard navigation: Users cannot use arrow keys to navigate suggestions or Escape to close the dropdown.
- Hover feedback: No visual indication when hovering over suggestions.
- Internationalization: The hardcoded
"org"label violates the i18n requirement in path instructions.
♻️ Suggested improvements
1. Add semantic role and hover styles:
{suggestions.map(org => (
<div
key={org.login}
+ role="button"
+ tabIndex={0}
onMouseDown={() => selectSuggestion(org.login)}
+ onKeyDown={(e) => e.key === 'Enter' && selectSuggestion(org.login)}
style={{
padding: '10px 12px',
cursor: 'pointer',
fontSize: 13,
display: 'flex',
justifyContent: 'space-between'
}}
+ onMouseEnter={(e) => e.currentTarget.style.background = 'var(--hover)'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
>2. Add keyboard navigation in useEffect:
Add handling for ArrowDown, ArrowUp, and Escape keys to navigate through suggestions.
3. Externalize "org" string:
As per path instructions, user-visible strings should be externalized to resource files for internationalization.
As per path instructions, user-visible strings should be externalized and the code should adhere to React best practices and accessibility standards.
🧰 Tools
🪛 ast-grep (0.44.0)
[warning] 145-145: A list component should have a key to prevent re-rendering
Context: {org.login}
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(list-component-needs-key)
[warning] 146-148: A list component should have a key to prevent re-rendering
Context: <span style={{ color: 'var(--text3)', fontSize: 11 }}>
org
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(list-component-needs-key)
🪛 React Doctor (0.5.8)
[warning] 135-135: Screen reader users can't tell this click handler is interactive because it has no role, so add a role or use a button or link.
Give clickable static elements a role, or use a button or link.
(no-static-element-interactions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/HomePage.jsx` around lines 117 - 154, The autocomplete dropdown
suggestions lack accessibility and keyboard navigation support. First, add
role="button" to the suggestion items (the divs with onMouseDown handlers) and
include hover styles for visual feedback. Second, implement keyboard navigation
by adding a useEffect hook that listens for ArrowDown, ArrowUp, and Escape key
events to navigate through suggestions and close the dropdown. Third,
externalize the hardcoded "org" string label used in the suggestions list to an
i18n resource file instead of keeping it as a literal string in the JSX,
following the path instructions for internationalization compliance.
Sources: Path instructions, Linters/SAST tools
| export const searchOrgs = async (query) => { | ||
| if (!query) return [] | ||
|
|
||
| const res = await fetch( | ||
| `https://api.github.com/search/users?q=${encodeURIComponent(query)}+type:org` | ||
| ) | ||
| if (!res.ok) return [] | ||
|
|
||
| const data = await res.json() | ||
| return data.items || [] | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift
Refactor to use the existing fetchWithCache infrastructure.
searchOrgs bypasses the established API helper pattern used by fetchOrg and fetchRepos. This causes several issues:
- No PAT authentication — Search is limited to 60 requests/hour instead of 5,000/hour with a token.
- No caching — Repeated autocomplete queries (e.g., typing "face", "faceb", "faceboo") make redundant API calls.
- No rate limit handling — Unlike
fetchWithCache(line 64), this won't throw'RATE_LIMIT'errors for downstream handling. - Inconsistent error handling — Returns
[]instead of throwing, unlike other helpers.
♻️ Proposed refactor to reuse fetchWithCache
-export const searchOrgs = async (query) => {
- if (!query) return []
-
- const res = await fetch(
- `https://api.github.com/search/users?q=${encodeURIComponent(query)}+type:org`
- )
- if (!res.ok) return []
-
- const data = await res.json()
- return data.items || []
-}
+export const searchOrgs = async (query, pat) => {
+ if (!query) return []
+
+ try {
+ const url = `https://api.github.com/search/users?q=${encodeURIComponent(query)}+type:org`
+ const data = await fetchWithCache(url, pat)
+ return data.items || []
+ } catch (err) {
+ // Return empty on errors (NOT_FOUND, RATE_LIMIT, etc.)
+ return []
+ }
+}This will require passing pat from HomePage through to searchOrgs. You can access it via useApp() context.
As per path instructions, the code should adhere to performance best practices.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/services/github.js` around lines 88 - 98, Refactor the searchOrgs
function to use the existing fetchWithCache infrastructure instead of making a
direct fetch call, following the same pattern as fetchOrg and fetchRepos
functions. The searchOrgs function currently bypasses authentication, caching,
and rate limit handling. To fix this, replace the direct fetch implementation
with a call to fetchWithCache, passing the GitHub search users API endpoint and
the query parameter. You will need to obtain the PAT token from the useApp()
context hook and pass it to fetchWithCache to enable proper authentication and
access the higher rate limits, along with the established caching and rate limit
error handling mechanisms.
Source: Path instructions
Addressed Issues:
Fixes #86
Screenshots/Recordings:
OrgExplorer.GitHub.Organization.Analytics.Repository.Insights.-.Comet.2026-06-23.12-00-08.mp4
Additional Notes:
Checklist
We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.
Summary by CodeRabbit
Release Notes
New Features
Documentation