Skip to content

Add autocomplete for team member addition #239

@bmbouter

Description

@bmbouter

Summary

The "Add Member" field on the Teams page is a plain text input with no guidance. Users must know the exact username. Add autocomplete that shows matching users as you type.

Design

Data Source

  • Fetch full user list from `GET /api/v1/users` once on input focus (lazy fetch, then cache)
  • Client-side substring filtering — no per-keystroke API calls
  • Refresh cache on each focus event to pick up new users

UI Pattern

  • Absolute-positioned `
    ` dropdown below the input
  • Max 8 visible results, then scroll
  • Match highlighting: matching substring in blue bold (``)

Matching

  • Case-insensitive substring match (`username.toLowerCase().includes(query)`)
  • Highlight matched portion

Selection

  • Click to select
  • Arrow keys to navigate + Enter to select
  • Escape closes dropdown
  • Typing exact username + Enter still works (existing behavior preserved)

Already-Members

  • Show existing team members dimmed (0.45 opacity, italic "already member" suffix)
  • Non-selectable (`pointer-events: none`)

Empty State

  • "No users found" in muted italic text

Accessibility

  • `role="combobox"` on input, `role="listbox"` on dropdown, `role="option"` on items
  • `aria-expanded`, `aria-activedescendant`, `aria-autocomplete="list"`
  • Keyboard navigation fully supported

CSS Specs

```css
.autocomplete-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: 4px;
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}

.autocomplete-item {
padding: 8px 12px;
color: #e2e8f0;
cursor: pointer;
font-size: 0.9rem;
border-bottom: 1px solid #334155;
}

.autocomplete-item:hover,
.autocomplete-item.selected {
background: #334155;
}

.autocomplete-match {
color: #3498db;
font-weight: 600;
}

.autocomplete-item.already-member {
opacity: 0.45;
cursor: default;
pointer-events: none;
}

.autocomplete-item.already-member::after {
content: " (already member)";
color: #64748b;
font-style: italic;
font-size: 0.8rem;
}

.autocomplete-empty {
padding: 12px;
color: #64748b;
text-align: center;
font-style: italic;
}
```

Implementation

JavaScript (`web/js/app.js`)

  • ~100 lines, self-contained function
  • Event listeners: `focus` (fetch/show), `input` (filter), `keydown` (arrows/enter/escape), `click` (select), `blur` with delay (close)
  • Dropdown markup created dynamically
  • Selected username populates the existing input field — existing form submission unchanged

CSS (`web/css/style.css`)

  • ~30 lines for the dropdown, items, highlights, states

HTML (`web/index.html`)

  • Add `position: relative` wrapper around the member input
  • No other HTML changes needed (dropdown is dynamic)

Tests

  • Functional test: add a valid user via autocomplete, verify member appears
  • Functional test: attempt to add already-member, verify rejection
  • Verify the input still works without autocomplete (direct typing + enter)

Docs

  • Update any teams documentation to mention autocomplete behavior

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions