Skip to content

Use shadcn InputGroup in ProjectSwitcher#2810

Merged
dimaMachina merged 6 commits intomainfrom
input-group
Mar 25, 2026
Merged

Use shadcn InputGroup in ProjectSwitcher#2810
dimaMachina merged 6 commits intomainfrom
input-group

Conversation

@dimaMachina
Copy link
Collaborator

before

image

after

image

@vercel
Copy link

vercel bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Mar 25, 2026 6:39am
agents-manage-ui Ready Ready Preview, Comment Mar 25, 2026 6:39am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agents-docs Skipped Skipped Mar 25, 2026 6:39am

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Mar 24, 2026

🦋 Changeset detected

Latest commit: 4813fe7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@inkeep/agents-manage-ui Patch
@inkeep/agents-api Patch
@inkeep/agents-cli Patch
@inkeep/agents-core Patch
@inkeep/agents-email Patch
@inkeep/agents-mcp Patch
@inkeep/agents-sdk Patch
@inkeep/agents-work-apps Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel vercel bot temporarily deployed to Preview – agents-docs March 24, 2026 14:28 Inactive
@github-actions github-actions bot deleted a comment from claude bot Mar 24, 2026
@pullfrog
Copy link
Contributor

pullfrog bot commented Mar 24, 2026

TL;DR — Adds the shadcn InputGroup compound component to the UI library and uses it in ProjectSwitcher to replace the ad-hoc search input layout with a structured, composable group.

Key changes

  • Add shadcn InputGroup component — introduces InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupInput, and InputGroupTextarea as reusable compound components with variant support for addon alignment, focus/error states, and dark mode.
  • Refactor ProjectSwitcher search input — replaces the manual div + Input + icon layout with InputGroup / InputGroupInput / InputGroupAddon, removing custom sizing and focus-ring overrides.
  • Add changeset — patch bump for @inkeep/agents-manage-ui documenting the new component.

Summary | 3 files | 6 commits | base: maininput-group

Before: The project search field in ProjectSwitcher was a bare Input wrapped in a flex div with a manually positioned Search icon and inline className overrides to suppress borders, shadows, and focus rings.
After: The search field uses the new InputGroup compound component — InputGroupInput handles the borderless/shadowless styling by default, and InputGroupAddon positions the Search icon with proper spacing and click-to-focus behavior.

The new input-group.tsx follows the shadcn compound-component pattern: a root InputGroup container manages shared border, ring, and error states via CSS has() selectors, while child components (InputGroupAddon, InputGroupInput, InputGroupButton, InputGroupText, InputGroupTextarea) compose freely inside it. Addon alignment is controlled by a data-align attribute driven by class-variance-authority variants (inline-start, inline-end, block-start, block-end).

input-group.tsx · project-switcher.tsx · bright-dogs-grin.md

Pullfrog  | View workflow run | Triggered by Pullfrogpullfrog.com𝕏

Copy link
Contributor

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

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

Clean refactor — the InputGroup component is a solid reusable primitive that replaces ad-hoc div + Input wiring. One import-ordering nit and an accessibility question.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow runpullfrog.com𝕏

DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input';
import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group';
Copy link
Contributor

Choose a reason for hiding this comment

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

The checked-out file shows this import ended up on line 20, after @/lib/query/projects. Biome's organizeImports expects @/components/ui/input-group to sort before @/components/ui/sidebar. Run pnpm format to auto-fix.

return (
// biome-ignore lint/a11y/useKeyWithClickEvents: use official shadcn example, todo: maybe refactor in the future
<div
role="group"
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: InputGroupAddon uses role="group" while its parent InputGroup already uses role="group". Nested ARIA groups imply a sub-grouping of related controls, but this element is a decorative icon container. Consider dropping role here or using role="presentation" to avoid misleading assistive technology.

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(1) Total Issues | Risk: Low

This PR cleanly introduces a new shadcn InputGroup compound component and refactors ProjectSwitcher to use it. The implementation follows established codebase patterns well — uses cva for variants, cn for class merging, data-slot attributes, and function declaration style matching other UI components.

🟡 Minor (1) 🟡

🟡 1) input-group.tsx:65 Redundant ARIA role on addon

Issue: InputGroupAddon has role="group" which is semantically incorrect — the addon is a visual container for icons/buttons, not a grouping of form controls.

Why: The parent InputGroup already has role="group" for the entire input group. Adding another role="group" on the addon can confuse assistive technologies.

Fix: Remove role="group" from the InputGroupAddon div.

Refs: WAI-ARIA group role

Inline Comments:

  • 🟡 Minor: input-group.tsx:65 Remove redundant role="group"

💭 Consider (1) 💭

💭 1) input-group.tsx:113-122 Add data-slot to InputGroupText

Issue: InputGroupText is missing a data-slot attribute unlike most other compound component parts.

Why: While ButtonGroupText also lacks data-slot, the majority of UI subcomponents include it for CSS targeting and identification.

Fix: Add data-slot="input-group-text" to the span.

Refs: card.tsx CardTitle pattern

Inline Comments:

  • 💭 Consider: input-group.tsx:113-122 Add data-slot attribute

✅ APPROVE

Summary: Clean implementation that follows established patterns. The component API is well-designed with proper composition support. The one Minor finding (redundant ARIA role) is a small accessibility improvement but doesn't block merge. Nice work simplifying the ProjectSwitcher search input! 🎉

Discarded (2)
Location Issue Reason Discarded
input-group.tsx:13-14 InputGroup role="group" without aria-label Valid pattern — the input inside has its own aria-label="Search projects", making a group-level label redundant for single-input groups
project-switcher.tsx:104 Search icon missing aria-hidden Lucide-react icons have aria-hidden="true" by default, so this is already handled
Reviewers (3)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-frontend 3 1 0 0 1 0 2
pr-review-consistency 3 0 1 0 1 0 0
pr-review-standards 0 0 0 0 0 0 0
Total 6 1 1 0 2 0 2

return (
// biome-ignore lint/a11y/useKeyWithClickEvents: use official shadcn example, todo: maybe refactor in the future
<div
role="group"
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Minor: Redundant role="group" on addon

Issue: InputGroupAddon has role="group" which is semantically incorrect for this element. The addon is not a grouping of interactive elements — it contains visual decorations or buttons.

Why: Using incorrect ARIA roles can confuse assistive technology users about the element's purpose. A role="group" suggests a collection of related form controls, but this addon just contains an icon or button. The parent InputGroup already has role="group" which is appropriate for the entire input group.

Fix: Remove role="group" from InputGroupAddon:

Suggested change
role="group"
<div

Refs:

Comment on lines +113 to +122
function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
className={cn(
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

💭 Consider: Add data-slot attribute for consistency

Issue: InputGroupText is missing a data-slot attribute unlike most other UI subcomponents in this codebase.

Why: Most compound component parts use data-slot for CSS targeting and component identification (e.g., CardTitle, AlertTitle, DialogTitle, FormItem). While ButtonGroupText also lacks data-slot (making this not unprecedented), the majority pattern includes it.

Fix: Consider adding data-slot="input-group-text" to the span element for consistency:

Suggested change
function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
className={cn(
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
);
function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
data-slot="input-group-text"
className={cn(
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
);
}

Refs:

@github-actions github-actions bot deleted a comment from claude bot Mar 24, 2026
@dimaMachina
Copy link
Collaborator Author

@claude add a changeset based on changes in this PR

@inkeep-internal-ci
Copy link
Contributor

inkeep-internal-ci bot commented Mar 24, 2026

Claude finished @dimaMachina's task in 1m 2s —— View job


Adding changeset

  • Create changeset for agents-manage-ui
  • Push to branch

Added a patch changeset for @inkeep/agents-manage-ui: "Add InputGroup compound component and use it in ProjectSwitcher search input"
·

Co-authored-by: Dimitri POSTOLOV <dimaMachina@users.noreply.github.com>
@itoqa
Copy link

itoqa bot commented Mar 24, 2026

Ito Test Report ✅

18 test cases ran. 18 passed.

The unified local verification run passed all 18/18 test cases with zero failures, confirming the ProjectSwitcher is stable, opens reliably on project-scoped pages, and showed no uncaught console/runtime errors across the covered scenarios. Key findings were that filtering works correctly by name, description, and case-insensitive matching (including blank/whitespace, no-description, no-match, and large-list states), navigation and recovery are robust (including unknown-project recovery, double-clicks, rapid open/close, and Back/Forward sync), keyboard/focus behavior is safely isolated to prevent accidental route changes, malicious script-like input is treated as inert text, and mobile behavior remains usable with transient search state resetting after refresh.

✅ Passed (18)
Category Summary Screenshot
Adversarial Rapid open/close cycles consistently reset the search query and restored the full project list. ADV-1
Adversarial Double-clicking a filtered project result navigated correctly and left the UI interactive without errors. ADV-3
Adversarial Malicious payload was treated as inert text with no dialog/crash, and clearing restored normal project results. ADV-4
Adversarial A→B switch plus browser Back/Forward kept URL and switcher trigger context in sync. ADV-5
Edge Blank query in the project switcher showed the full project list and no empty-state message. EDGE-1
Edge Entering whitespace-only input preserved the same project results as a blank query. EDGE-2
Edge Lowercase query matched mixed-case project name, confirming case-insensitive filtering. EDGE-3
Edge Filtering by a no-description project name remained stable and returned the expected project without crashing. EDGE-4
Edge A guaranteed no-match query showed "No projects found," and clearing input restored the project list. EDGE-5
Edge Recovered from "Project not found" route by switching to a valid project and landing on /default/projects/edge-six-project/agents. EDGE-6
Edge Verified the project switcher dropdown remains height-capped and scrollable for large project lists, with no product defect confirmed. EDGE-7
Journey After entering a non-empty query and refreshing, reopening the switcher showed an empty search field and full project list. JOURNEY-1
Logic Clicking the search icon addon focused the input, immediate typing worked, and sustained keyboard input did not trigger accidental navigation. LOGIC-1
Logic Source-backed verification confirmed key events in the search input are stopped from propagating to dropdown keyboard handlers. LOGIC-2
Mobile Mobile switcher opened from the sidebar on 390x844, filtering and project switching worked, and transient search state cleared after refresh. MOBILE-1
Happy-path Project-scoped Agents page loaded, project switcher opened on first click, search input was enabled, and no uncaught console errors were observed. ROUTE-1
Happy-path Project search filtered by name substring and selecting the result navigated to the selected project agents route. ROUTE-2
Happy-path Description-token search returned the expected project in the switcher results. ROUTE-3

Commit: 8b1eaa9

View Full Run


Tell us how we did: Give Ito Feedback

@dimaMachina
Copy link
Collaborator Author

@claude audit where InputGroup can be used too instead of manual placing

@inkeep-internal-ci
Copy link
Contributor

inkeep-internal-ci bot commented Mar 24, 2026

Claude finished @dimaMachina's task in 1m 33s —— View job


Auditing InputGroup usage opportunities

  • Read the new InputGroup component to understand its API
  • Search for manual input+icon patterns across the UI codebase
  • Report findings

Found 5 locations where InputGroup can replace manual div + absolute-positioned icon + padded Input patterns:

1. components/datasets/test-case-filters.tsx (lines 49-57)

Pattern: relative div → absolute Search icon → Input with pl-9

<div className="relative flex-1 max-w-sm">
  <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
  <Input ... className="pl-9 h-9" />
</div>

2. components/evaluation-jobs/evaluation-results-filters.tsx (lines 64-72)

Pattern: Same — relative div → absolute Search icon → Input with pl-9

<div className="relative flex-1 min-w-[200px] max-w-sm">
  <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
  <Input ... className="pl-9 h-9" />
</div>

3. components/credentials/views/nango-providers-grid.tsx (lines 88-95)

Pattern: Same — relative div → absolute Search icon → Input with pl-10

<div className="relative max-w-md mx-auto">
  <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
  <Input ... className="pl-10" />
</div>

4. components/traces/conversation-stats/conversation-stats-card.tsx (lines 118-143)

Pattern: Dual-icon — Search left + conditional X clear button right → Input with pl-8 pr-8

<div className="relative flex-1 max-w-md">
  <Search className="absolute left-2 top-1/2 ..." />
  <Input ... className="pl-8 pr-8" />
  {localQuery && (
    <Button ... className="absolute right-1 top-1/2 ..."><X /></Button>
  )}
</div>

5. features/work-apps/slack/components/agent-configuration-card/channel-defaults-section.tsx (lines 248-266)

Pattern: Same dual-icon pattern as #4Search left + conditional X clear button right

<div className="relative flex-1 max-w-md">
  <Search className="absolute left-2 top-1/2 ..." />
  <Input ... className="pl-8 pr-8" />
  {channelSearchQuery && (
    <Button ... className="absolute right-1 top-1/2 ..."><X /></Button>
  )}
</div>

Summary

Pattern Count Files
Search icon (left) only 3 test-case-filters, evaluation-results-filters, nango-providers-grid
Search icon (left) + clear button (right) 2 conversation-stats-card, channel-defaults-section

All 5 use the same anti-pattern: a relative container with absolute-positioned icons and manual padding offsets (pl-8/pl-9/pl-10, pr-8) on the Input. These are direct candidates for InputGroup + InputGroupAddon + InputGroupInput (and InputGroupButton for the clear buttons).
| Branch: input-group

Copy link
Contributor

@sarah-inkeep sarah-inkeep left a comment

Choose a reason for hiding this comment

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

looks good, one small nit but could we remove the shadow on the input in the project switcher
(it's kinda hard to see in the screenshot)
Image

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) Total Issues | Risk: Low

This is a delta review scoped to changes since the last automated review (c99af215ac77).

Changes Reviewed

Commit Description
8b1eaa93d Add changeset for InputGroup component
4813fe731 fix review

Delta: 2 files, +6/-1 lines

Assessment

Changeset — Properly formatted patch bump for @inkeep/agents-manage-ui with clear description.

Shadow fix — Added shadow-none to InputGroup className in project-switcher.tsx, addressing sarah-inkeep's approved review feedback to remove the subtle shadow on the search input.

🕐 Pending Recommendations (2)

Prior issues raised in the previous automated review that remain unaddressed:


✅ APPROVE

Summary: Delta changes are clean — the changeset is properly formatted and the shadow fix correctly addresses the human reviewer's feedback. The PR is ready to merge. The prior accessibility suggestions (redundant ARIA role, missing data-slot) remain valid considerations for a follow-up but don't block merge. Nice work on this clean InputGroup component! 🎉

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
N/A — delta review

Note: Delta review — no subagents dispatched as changes were minimal (changeset + 1-line fix addressing human feedback).

@github-actions github-actions bot deleted a comment from claude bot Mar 25, 2026
@dimaMachina dimaMachina enabled auto-merge March 25, 2026 06:46
@dimaMachina dimaMachina added this pull request to the merge queue Mar 25, 2026
Merged via the queue into main with commit 82bd424 Mar 25, 2026
17 of 18 checks passed
@dimaMachina dimaMachina deleted the input-group branch March 25, 2026 06:55
@itoqa
Copy link

itoqa bot commented Mar 25, 2026

Ito Test Report ✅

20 test cases ran. 20 passed.

The unified test run passed completely with 20/20 test cases green and no confirmed production defects, validating the project switcher’s new InputGroup search UI (search icon addon, aria-label, placeholder) across project-scoped routes including agents, components, skills, artifacts, settings, direct deep links, and mobile viewport navigation. Key behaviors were consistently verified: name and description filtering, correct no-results empty state, reset on close/reopen and refresh, stable back/forward history behavior, keyboard containment, icon-to-input focus, resilience under rapid toggling/double-click/rapid type-delete, safe handling of HTML/script payloads plus long and Unicode/RTL queries, and correct authorization gating where non-admin users could search but could not see a Create project action.

✅ Passed (20)
Category Summary Screenshot
Adversarial HTML/script payload was treated as plain text and no script execution was observed. ADV-1
Adversarial A 2000-character query was entered and cleared without freezing; normal search remained responsive. ADV-2
Adversarial Mixed RTL/unicode and emoji input did not break layout, and clearing restored normal state. ADV-3
Authz Non-admin member could use switcher search but did not see the Create project action. AUTHZ-1
Edge Closing and reopening the switcher resets the search input and restores the full list. EDGE-1
Edge Whitespace-only input behaves like an empty query and preserves the baseline project row count. EDGE-2
Edge ArrowDown/ArrowUp/Enter in the search field do not trigger project navigation or selection. EDGE-3
Edge Clicking the search icon keeps the menu open and focuses the input for immediate typing. EDGE-4
Edge Refreshing mid-interaction reset the switcher search state and restored the unfiltered project list. EDGE-5
Edge Back/Forward navigation preserved switcher open, input, and filtering behavior across history states. EDGE-6
Flow Direct deep-link entry still allowed switcher search and project selection navigation to a working agents page. FLOW-1
Mobile Mobile viewport switcher remained usable: search filtered results and project-row tap navigated to the expected agents route without 404 after environment remediation. MOBILE-1
Rapid Rapid trigger toggling did not desynchronize dropdown state after auth/environment remediation. RAPID-1
Rapid Fast double-click on a filtered project row produced a single effective navigation with an interactive destination page. RAPID-2
Rapid Repeated rapid type/delete cycles kept no-results and reset behavior deterministic. RAPID-3
Happy-path Project-scoped agents route renders the project switcher search InputGroup with visible search addon icon and expected accessibility labeling. ROUTE-1
Happy-path Name search activ filtered projects and selecting the result navigated correctly to the target project agents route. ROUTE-2
Happy-path Description-keyword search filtered to the expected project even when the keyword was not in the project name. ROUTE-3
Happy-path Unmatched query showed the expected No projects found empty state in the switcher list. ROUTE-4
Happy-path Project switcher remains functional across agents, components, skills, artifacts, and settings routes with consistent InputGroup search row behavior. ROUTE-5

Commit: 4813fe7

View Full Run


Tell us how we did: Give Ito Feedback

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.

2 participants