Skip to content

feat: add search autocomplete dropdown for mobile navbar#1010

Open
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/search-autocomplete-mobile
Open

feat: add search autocomplete dropdown for mobile navbar#1010
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/search-autocomplete-mobile

Conversation

@ayesha1145
Copy link

@ayesha1145 ayesha1145 commented Mar 6, 2026

Extends the search autocomplete feature to the mobile navigation menu.

  • web/templates/base.html: added id and autocomplete="off" to mobile search input, dropdown container inside relative parent, debounced fetch with AbortController, safe DOM construction, ARIA roles, Escape/outside-click dismissal

Reuses the existing search_autocomplete endpoint — no backend changes needed.

Summary by CodeRabbit

  • New Features
    • Mobile search gains autocomplete suggestions as you type, integrated into the mobile menu. Suggestions show icons, course titles, and teacher names and become active after 2+ characters. Results are clickable to navigate directly, and the suggestion dropdown can be dismissed by clicking outside or pressing Escape.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

👀 Peer Review Required

Hi @ayesha1145! This pull request does not yet have a peer review.

Before this PR can be merged, please request a review from one of your peers:

  • Go to the PR page and click "Reviewers" on the right sidebar.
  • Select a team member or contributor to review your changes.
  • Once they approve, this reminder will be automatically removed.

Thank you for contributing! 🎉

@github-actions github-actions bot added the files-changed: 1 PR changes 1 file label Mar 6, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

Warning

Rate limit exceeded

@ayesha1145 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 22 minutes and 15 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 386a2a40-de62-4b91-9115-16f35e296df3

📥 Commits

Reviewing files that changed from the base of the PR and between 192d6a9 and 10dada3.

📒 Files selected for processing (1)
  • web/templates/base.html

Walkthrough

Adds mobile search autocomplete to the base template: a mobile input and hidden dropdown plus client-side JavaScript that debounces input, cancels in-flight requests, fetches suggestions from search_autocomplete, renders items, and handles closing on outside click or Escape.

Changes

Cohort / File(s) Summary
Mobile Search Autocomplete
web/templates/base.html
Adds mobile search input search-input-mobile and hidden dropdown autocomplete-dropdown-mobile; introduces client-side autocomplete script with 250ms debounce, minimum 2-character queries, AbortController-based request cancellation, fetch of suggestions, rendering of icon/title/teacher, error/empty-result handling, and closing on outside click or Escape.

Sequence Diagram

sequenceDiagram
    actor User
    participant Browser as Client (JavaScript)
    participant Server

    User->>Browser: Type in mobile search input
    Note over Browser: Debounce (250ms)
    Browser->>Browser: If query length >= 2
    alt valid query
        Browser->>Server: GET /search_autocomplete?q=...
        alt success
            Server-->>Browser: suggestions[]
            Browser->>Browser: Render dropdown (icon, title, teacher)
            Browser->>Browser: Show dropdown
        else failure or empty
            Browser->>Browser: Clear/hide dropdown
        end
    else short query
        Browser->>Browser: Hide/ignore dropdown
    end
    User->>Browser: Click suggestion / press Escape / click outside
    Browser->>Browser: Navigate or close dropdown
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add search autocomplete dropdown for mobile navbar' accurately and specifically describes the main change in the pull request, which adds mobile-specific autocomplete functionality to the search feature in the navbar.
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 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.

@ayesha1145 ayesha1145 force-pushed the feat/search-autocomplete-mobile branch from 817618b to 192d6a9 Compare March 6, 2026 03:39
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: 2

Caution

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

⚠️ Outside diff range comments (1)
web/templates/base.html (1)

547-557: ⚠️ Potential issue | 🟠 Major

Align this with a real combobox pattern.

role="listbox" on Line 557 is not enough here: the input never exposes aria-controls/aria-expanded, and the popup later renders anchors as role="option" on Lines 1155-1159. That mixes link and listbox semantics, so assistive tech will not get a reliable autocomplete model. Either implement the full combobox pattern end-to-end, or treat this as a plain list of links and drop the listbox/option roles; if you keep links, add visible focus:/focus-visible: styles to each row.

As per coding guidelines, "Ensure proper HTML structure and accessibility in templates", "Include proper ARIA labels where needed for accessibility", and "Add hover/focus states for interactive elements".

Also applies to: 1155-1159

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/base.html` around lines 547 - 557, The autocomplete dropdown
currently uses role="listbox" on the element with
id="autocomplete-dropdown-mobile" while the input id="search-input-mobile" does
not expose aria-controls/aria-expanded and the suggestion anchors are rendered
with role="option", which mixes semantics; either convert this to a full ARIA
combobox pattern by adding aria-haspopup="listbox", aria-expanded and
aria-controls on search-input-mobile, manage aria-activedescendant on the input
and ensure each suggestion has an id (not role="option" on anchors) and keyboard
handling in the JS, or remove role="listbox"/role="option" entirely and treat
suggestions as plain links styled for keyboard focus (add visible
focus:/focus-visible: and hover styles on the suggestion anchors). Update the
template around autocomplete-dropdown-mobile and the suggestion anchor
generation (the current anchors at the later block) to follow one of these two
options consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/base.html`:
- Line 1145: The template uses a reverse for the URL name search_autocomplete
which does not exist (only course_search is registered), causing NoReverseMatch
when rendering base.html; either add a URL pattern in your URLconf with
name="search_autocomplete" pointing to the existing autocomplete view (the same
view/function/class that implements course_search/autocomplete) or update the
template call to use the existing URL name course_search (and confirm the view
accepts the q parameter used by fetch). Ensure the chosen resolution keeps the
view name and query parameter behavior consistent across the template
(search_autocomplete reference) and the URLconf.
- Around line 1137-1176: When the input becomes shorter than 2 characters, abort
any in-flight request and cancel the debounce so stale fetches cannot repopulate
the dropdown: inside the query.length < 2 branch, call currentController.abort()
if currentController exists, clearTimeout(debounceTimer) (and set debounceTimer
= null if you track it), then clear/reset currentController, hide/clear dropdown
as already done; this ensures any pending fetch created in the debounce/fetch
block (using currentController and debounceTimer) is invalidated before
returning.

---

Outside diff comments:
In `@web/templates/base.html`:
- Around line 547-557: The autocomplete dropdown currently uses role="listbox"
on the element with id="autocomplete-dropdown-mobile" while the input
id="search-input-mobile" does not expose aria-controls/aria-expanded and the
suggestion anchors are rendered with role="option", which mixes semantics;
either convert this to a full ARIA combobox pattern by adding
aria-haspopup="listbox", aria-expanded and aria-controls on search-input-mobile,
manage aria-activedescendant on the input and ensure each suggestion has an id
(not role="option" on anchors) and keyboard handling in the JS, or remove
role="listbox"/role="option" entirely and treat suggestions as plain links
styled for keyboard focus (add visible focus:/focus-visible: and hover styles on
the suggestion anchors). Update the template around autocomplete-dropdown-mobile
and the suggestion anchor generation (the current anchors at the later block) to
follow one of these two options consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c19e6393-6fcc-4faf-9abc-0649b3e87cb2

📥 Commits

Reviewing files that changed from the base of the PR and between c94caf8 and 817618b.

📒 Files selected for processing (1)
  • web/templates/base.html

@ayesha1145 ayesha1145 force-pushed the feat/search-autocomplete-mobile branch from 192d6a9 to 10dada3 Compare March 6, 2026 03:43
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

Caution

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

⚠️ Outside diff range comments (1)
web/templates/base.html (1)

546-557: ⚠️ Potential issue | 🟡 Minor

Add ARIA attributes to the input for complete accessibility.

The dropdown has role="listbox" but the input lacks aria-controls and aria-expanded attributes. Screen readers won't announce the relationship between the input and the suggestions dropdown.

♿ Proposed fix for accessibility
                  <input type="text"
                         id="search-input-mobile"
                         autocomplete="off"
+                        aria-controls="autocomplete-dropdown-mobile"
+                        aria-expanded="false"
+                        aria-autocomplete="list"
                         name="q"

Then update the JavaScript to toggle aria-expanded:

// When showing dropdown:
input.setAttribute('aria-expanded', 'true');
// When hiding dropdown:
input.setAttribute('aria-expanded', 'false');

As per coding guidelines: "Include proper ARIA labels where needed for accessibility."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/base.html` around lines 546 - 557, Add ARIA linkage and state
to the mobile search input: update the input element with id
"search-input-mobile" to include aria-controls="autocomplete-dropdown-mobile"
and an initial aria-expanded="false" (and aria-haspopup="listbox" if desired),
and then update the JS that shows/hides the element with id
"autocomplete-dropdown-mobile" to set
document.getElementById('search-input-mobile').setAttribute('aria-expanded','true')
when showing and setAttribute('aria-expanded','false') when hiding so screen
readers can discover the relationship and current state.
♻️ Duplicate comments (2)
web/templates/base.html (2)

1145-1145: ⚠️ Potential issue | 🔴 Critical

The URL name search_autocomplete does not exist and will break template rendering.

The {% url 'search_autocomplete' %} tag will raise NoReverseMatch because this URL is not defined in web/urls.py. Since base.html is extended by every page, this will break the entire site.

Either add the URL pattern in urls.py:

path("courses/search/autocomplete/", views.search_autocomplete, name="search_autocomplete"),

Or if an existing endpoint returns JSON with the expected structure {"results": [{"title": "...", "teacher": "...", "url": "..."}]}, update the template to use that URL name instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/base.html` at line 1145, The template uses the non-existent URL
name 'search_autocomplete' in base.html (fetch("{% url 'search_autocomplete'
%}...")), causing NoReverseMatch; either add a URL pattern that names the
endpoint 'search_autocomplete' pointing to the view function (e.g.,
views.search_autocomplete) in your URL conf, or change the template to reference
an existing URL name that returns the expected JSON shape
({"results":[...]})—update the path name in urls.py or the string in the {% url
%} call accordingly so the template resolves at render time.

1137-1176: ⚠️ Potential issue | 🟠 Major

Abort in-flight requests and guard against stale responses when query becomes too short.

When input drops below 2 characters (Lines 1137-1140), the pending fetch continues running. If it resolves later, it will repopulate the dropdown with stale suggestions even though the input is now empty or too short. Additionally, the catch block should ignore AbortError to avoid clearing the dropdown unnecessarily when requests are intentionally cancelled.

🔧 Proposed fix
     if (query.length < 2) {
+        if (currentController) {
+            currentController.abort();
+            currentController = null;
+        }
         dropdown.classList.add('hidden');
-        dropdown.innerHTML = '';
+        dropdown.replaceChildren();
         return;
     }
     debounceTimer = setTimeout(() => {
         if (currentController) currentController.abort();
         currentController = new AbortController();
+        const requestQuery = query;
         fetch("{% url 'search_autocomplete' %}" + '?q=' + encodeURIComponent(query), { signal: currentController.signal })
             .then(r => r.json())
             .then(data => {
+                // Guard against stale response
+                if (input.value.trim() !== requestQuery) return;
                 if (!data.results || data.results.length === 0) {
                     dropdown.classList.add('hidden');
-                    dropdown.innerHTML = '';
+                    dropdown.replaceChildren();
                     return;
                 }
                 // ... rest unchanged
             })
-            .catch(() => {
+            .catch((error) => {
+                if (error.name === 'AbortError') return;
                 dropdown.classList.add('hidden');
                 dropdown.replaceChildren();
             });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/base.html` around lines 1137 - 1176, When query.length < 2,
ensure any in-flight request is aborted and its response won't repopulate the
dropdown: call currentController.abort() (if set), clear debounceTimer, set
currentController = null, and then hide/clear the dropdown as you already do. In
the fetch promise chain, update the catch handler to detect and ignore
AbortError (e.g., if (err.name === 'AbortError') return) so intentional
cancellations don't clear the dropdown, and only perform
dropdown.classList.add('hidden') / dropdown.replaceChildren() for non-abort
errors; keep references to debounceTimer and currentController to locate the
logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/base.html`:
- Around line 1186-1188: Add keyboard navigation to the existing input keydown
handler so users can traverse and select dropdown items: introduce an
activeIndex variable (initialized -1) and on 'ArrowDown'/'ArrowUp' update
activeIndex within bounds of dropdown.querySelectorAll('a[role="option"]')
(named items), call an updateActiveItem(items) helper to set aria-selected and
the focused CSS class, handle 'Enter' to trigger items[activeIndex].click(), and
reset activeIndex and hide the dropdown on 'Escape'; modify the current
input.addEventListener('keydown', ...) logic to incorporate these branches and
call updateActiveItem when activeIndex changes.

---

Outside diff comments:
In `@web/templates/base.html`:
- Around line 546-557: Add ARIA linkage and state to the mobile search input:
update the input element with id "search-input-mobile" to include
aria-controls="autocomplete-dropdown-mobile" and an initial
aria-expanded="false" (and aria-haspopup="listbox" if desired), and then update
the JS that shows/hides the element with id "autocomplete-dropdown-mobile" to
set
document.getElementById('search-input-mobile').setAttribute('aria-expanded','true')
when showing and setAttribute('aria-expanded','false') when hiding so screen
readers can discover the relationship and current state.

---

Duplicate comments:
In `@web/templates/base.html`:
- Line 1145: The template uses the non-existent URL name 'search_autocomplete'
in base.html (fetch("{% url 'search_autocomplete' %}...")), causing
NoReverseMatch; either add a URL pattern that names the endpoint
'search_autocomplete' pointing to the view function (e.g.,
views.search_autocomplete) in your URL conf, or change the template to reference
an existing URL name that returns the expected JSON shape
({"results":[...]})—update the path name in urls.py or the string in the {% url
%} call accordingly so the template resolves at render time.
- Around line 1137-1176: When query.length < 2, ensure any in-flight request is
aborted and its response won't repopulate the dropdown: call
currentController.abort() (if set), clear debounceTimer, set currentController =
null, and then hide/clear the dropdown as you already do. In the fetch promise
chain, update the catch handler to detect and ignore AbortError (e.g., if
(err.name === 'AbortError') return) so intentional cancellations don't clear the
dropdown, and only perform dropdown.classList.add('hidden') /
dropdown.replaceChildren() for non-abort errors; keep references to
debounceTimer and currentController to locate the logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 74a49421-1346-4e4a-9512-cf0772c32008

📥 Commits

Reviewing files that changed from the base of the PR and between 817618b and 192d6a9.

📒 Files selected for processing (1)
  • web/templates/base.html

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

💬 Unresolved Review Conversations

Hi @ayesha1145! 👋

This pull request currently has 3 unresolved review conversations.

Please address all review feedback and push a new commit to resolve them before this PR can be merged.

Steps to resolve:

  1. Review each comment thread in the "Files changed" tab.
  2. Make the necessary changes to your code.
  3. Reply to each conversation to explain your changes or ask for clarification.
  4. Click "Resolve conversation" once the feedback has been addressed.
  5. Push a new commit with your changes.

Once all conversations are resolved, this notice will be removed automatically. Thank you! 🙏

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This PR has 3 unresolved review conversations. Please resolve them before this PR can be merged.

@ayesha1145
Copy link
Author

Addressed CodeRabbit comments:

  1. Added currentController.abort() in the query.length < 2 branch to cancel any in-flight request when input is cleared
  2. search_autocomplete URL is registered in web/urls.py (added in PR feat: add search autocomplete dropdown for courses #1007) — this is not a missing route

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

files-changed: 1 PR changes 1 file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant