feat: limit badges to 2 with hover/click popover in UserListTable#35
Conversation
WalkthroughA new LimitedBadges UI component is introduced to render badges with overflow handling via a popover. ResponseValueCell is refactored to use LimitedBadges instead of Badge and HoverCard components, and its rowId prop is removed. The hook useInsightsColumns is updated to stop passing rowId. UserListTable exports are reorganized from default to named exports with type refinements. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/web/components/ui/LimitedBadges.tsx`:
- Around line 68-97: In LimitedBadges, the overflow items in hiddenItems are
rendered as plain <span> elements so their onClick handlers never fire and the
key uses only item.label which can collide; update the hiddenItems rendering
inside PopoverContent to render each entry as an interactive element (e.g.,
Button or Badge with onClick={item.onClick} and the same hover/mouse handlers
like handleMouseEnter/handleMouseLeave) and replace the unstable key
(item.label) with a stable identifier such as item.id or a compound key like
`${item.id ?? item.label}-${index}` to avoid collisions and ensure clicks
trigger the intended actions.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/components/ui/LimitedBadges.tsxapps/web/modules/insights/components/ResponseValueCell.tsxapps/web/modules/insights/hooks/useInsightsColumns.tsxapps/web/modules/users/components/UserTable/UserListTable.tsx
💤 Files with no reviewable changes (1)
- apps/web/modules/insights/hooks/useInsightsColumns.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/modules/insights/components/ResponseValueCell.tsx (1)
apps/web/components/ui/LimitedBadges.tsx (1)
LimitedBadges(107-107)
🔇 Additional comments (6)
apps/web/modules/insights/components/ResponseValueCell.tsx (1)
1-21: LGTM: simplified rendering via LimitedBadges.apps/web/modules/users/components/UserTable/UserListTable.tsx (5)
3-65: LGTM: updated imports, explicit return types, and named exports.Also applies to: 124-159, 746-747
200-266: LGTM: attribute values now render consistently via LimitedBadges.
282-518: LGTM: column cell typings and badge rendering are consistent.
541-543: LGTM: table config tweaks and download handler typing.Also applies to: 581-632
666-734: LGTM: bulk-action gating and analytics capture.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| {visibleItems.map((item, index) => ( | ||
| <Badge key={item.label} variant={item.variant || "gray"} onClick={item.onClick}> | ||
| {item.label} | ||
| </Badge> | ||
| ))} | ||
| {hasHiddenItems && ( | ||
| <Popover open={isOpen} onOpenChange={setIsOpen}> | ||
| <PopoverTrigger asChild> | ||
| <Button | ||
| color="minimal" | ||
| className="h-auto p-0 border-0 hover:border-0" | ||
| aria-label={`Show ${hiddenItems.length} more items`} | ||
| onMouseEnter={handleMouseEnter} | ||
| onMouseLeave={handleMouseLeave}> | ||
| <Badge variant="gray">+{hiddenItems.length}</Badge> | ||
| </Button> | ||
| </PopoverTrigger> | ||
| <PopoverContent | ||
| side="bottom" | ||
| align="start" | ||
| className="w-fit p-2" | ||
| onMouseEnter={handleMouseEnter} | ||
| onMouseLeave={handleMouseLeave}> | ||
| <div className="flex flex-col gap-1"> | ||
| {hiddenItems.map((item, index) => ( | ||
| <span | ||
| key={item.label} | ||
| className="text-default cursor-pointer text-sm hover:text-emphasis"> | ||
| {item.label} | ||
| </span> |
There was a problem hiding this comment.
Hidden overflow badges aren’t clickable (and keys may collide).
Overflow items are rendered as plain spans, so onClick handlers never fire (e.g., team filter actions). Also, using item.label alone as a key can collide when labels repeat. Consider rendering overflow items as buttons and using a stable key.
🛠️ Proposed fix
- {visibleItems.map((item, index) => (
- <Badge key={item.label} variant={item.variant || "gray"} onClick={item.onClick}>
+ {visibleItems.map((item, index) => (
+ <Badge key={`${item.label}-${index}`} variant={item.variant || "gray"} onClick={item.onClick}>
{item.label}
</Badge>
))}
@@
- {hiddenItems.map((item, index) => (
- <span
- key={item.label}
- className="text-default cursor-pointer text-sm hover:text-emphasis">
- {item.label}
- </span>
- ))}
+ {hiddenItems.map((item, index) => (
+ <button
+ key={`${item.label}-${index}`}
+ type="button"
+ onClick={item.onClick}
+ disabled={!item.onClick}
+ className={`text-default text-sm bg-transparent p-0 border-0 ${
+ item.onClick ? "cursor-pointer hover:text-emphasis" : "cursor-default"
+ }`}>
+ {item.label}
+ </button>
+ ))}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {visibleItems.map((item, index) => ( | |
| <Badge key={item.label} variant={item.variant || "gray"} onClick={item.onClick}> | |
| {item.label} | |
| </Badge> | |
| ))} | |
| {hasHiddenItems && ( | |
| <Popover open={isOpen} onOpenChange={setIsOpen}> | |
| <PopoverTrigger asChild> | |
| <Button | |
| color="minimal" | |
| className="h-auto p-0 border-0 hover:border-0" | |
| aria-label={`Show ${hiddenItems.length} more items`} | |
| onMouseEnter={handleMouseEnter} | |
| onMouseLeave={handleMouseLeave}> | |
| <Badge variant="gray">+{hiddenItems.length}</Badge> | |
| </Button> | |
| </PopoverTrigger> | |
| <PopoverContent | |
| side="bottom" | |
| align="start" | |
| className="w-fit p-2" | |
| onMouseEnter={handleMouseEnter} | |
| onMouseLeave={handleMouseLeave}> | |
| <div className="flex flex-col gap-1"> | |
| {hiddenItems.map((item, index) => ( | |
| <span | |
| key={item.label} | |
| className="text-default cursor-pointer text-sm hover:text-emphasis"> | |
| {item.label} | |
| </span> | |
| {visibleItems.map((item, index) => ( | |
| <Badge key={`${item.label}-${index}`} variant={item.variant || "gray"} onClick={item.onClick}> | |
| {item.label} | |
| </Badge> | |
| ))} | |
| {hasHiddenItems && ( | |
| <Popover open={isOpen} onOpenChange={setIsOpen}> | |
| <PopoverTrigger asChild> | |
| <Button | |
| color="minimal" | |
| className="h-auto p-0 border-0 hover:border-0" | |
| aria-label={`Show ${hiddenItems.length} more items`} | |
| onMouseEnter={handleMouseEnter} | |
| onMouseLeave={handleMouseLeave}> | |
| <Badge variant="gray">+{hiddenItems.length}</Badge> | |
| </Button> | |
| </PopoverTrigger> | |
| <PopoverContent | |
| side="bottom" | |
| align="start" | |
| className="w-fit p-2" | |
| onMouseEnter={handleMouseEnter} | |
| onMouseLeave={handleMouseLeave}> | |
| <div className="flex flex-col gap-1"> | |
| {hiddenItems.map((item, index) => ( | |
| <button | |
| key={`${item.label}-${index}`} | |
| type="button" | |
| onClick={item.onClick} | |
| disabled={!item.onClick} | |
| className={`text-default text-sm bg-transparent p-0 border-0 ${ | |
| item.onClick ? "cursor-pointer hover:text-emphasis" : "cursor-default" | |
| }`}> | |
| {item.label} | |
| </button> | |
| ))} |
🤖 Prompt for AI Agents
In `@apps/web/components/ui/LimitedBadges.tsx` around lines 68 - 97, In
LimitedBadges, the overflow items in hiddenItems are rendered as plain <span>
elements so their onClick handlers never fire and the key uses only item.label
which can collide; update the hiddenItems rendering inside PopoverContent to
render each entry as an interactive element (e.g., Button or Badge with
onClick={item.onClick} and the same hover/mouse handlers like
handleMouseEnter/handleMouseLeave) and replace the unstable key (item.label)
with a stable identifier such as item.id or a compound key like `${item.id ??
item.label}-${index}` to avoid collisions and ensure clicks trigger the intended
actions.
Benchmark PR from agentic-review-benchmarks#8
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.