Skip to content

Conversation

@eunjae-lee
Copy link
Contributor

@eunjae-lee eunjae-lee commented Jan 7, 2026

What does this PR do?

Limits the number of badges displayed in the "teams" column and attribute columns in the UserListTable to a maximum of 2. When there are more than 2 badges, only the first 2 are shown with a "+N" badge that opens a popover to display all hidden items.

Changes

New Component: LimitedBadges

Created a reusable LimitedBadges component in apps/web/components/ui/LimitedBadges.tsx that:

  • Accepts an array of BadgeItem objects (label, variant, onClick)
  • Shows up to maxVisible badges (default: 2) with a "+N" indicator for overflow
  • Opens a popover on hover for desktop and click for mobile (uses useMediaQuery to detect device type)
  • Uses useMemo and useCallback for performance optimization
  • Uses array index as the React key (simplified API - no id field required)

UserListTable Updates

  • Teams column now uses LimitedBadges to limit displayed team badges
  • Attribute columns now use LimitedBadges with a simplified label format showing value, weight percentage, and group indicator (e.g., "Value 100% (group)")
  • Added explicit TypeScript types to callback parameters for Biome lint compliance
  • Converted ternary operators to if-else statements for Biome noTernary compliance
  • The "pending" status badge is rendered separately and not counted toward the limit

ResponseValueCell Updates

  • Refactored to use the shared LimitedBadges component instead of inline badge rendering
  • Removed rowId parameter (no longer needed since array index is used as key)

Demo

Screenshot 2026-01-08 at 16 25 33

Updates since last revision

  • Removed id field from BadgeItem type - component now uses array index as the React key for simplicity
  • Removed unused rowId parameter from ResponseValueCell component
  • Fixed Biome lint warnings by converting ternary operators to if-else statements
  • Fixed import ordering issues

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • N/A - I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A - no documentation changes needed.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Navigate to the organization members page (Settings > Organizations > Members)
  2. Find users that belong to more than 2 teams
  3. Verify only 2 team badges are shown with a "+N" badge for remaining teams
  4. On desktop: Hover over the "+N" badge to see the popover appear, move mouse away to close
  5. On mobile (or with touch simulation): Tap the "+N" badge to toggle the popover
  6. For attribute columns, verify the same behavior applies when a user has more than 2 attribute values
  7. Verify the popover stays open when moving mouse from the trigger to the popover content

Human Review Checklist

  • Verify hover opens/closes the popover correctly on desktop
  • Verify click/tap opens/closes the popover on mobile devices
  • Test edge cases: 0, 1, 2, and 3+ badges
  • Confirm popover stays open when hovering over its content (not just the trigger)
  • Verify using array index as key: Confirm no rendering issues when badge lists change
  • Verify attribute column rendering: Weight and group indicator now appear in a single label format (e.g., "Value 100% (group)") instead of separate badges - confirm this looks acceptable
  • Verify the existing ResponseValueCell in insights module still works correctly (rowId parameter was removed)

Checklist

  • My code follows the style guidelines of this project
  • I have checked if my changes generate no new warnings

Link to Devin run

https://app.devin.ai/sessions/f4de6199ef3841b884221abf6e69a62a

Requested by

@eunjae-lee (eunjae@cal.com)

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@vercel
Copy link

vercel bot commented Jan 7, 2026

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

4 Skipped Deployments
Project Deployment Review Updated (UTC)
api-v2 Ignored Ignored Preview Jan 9, 2026 3:42am
cal Ignored Ignored Jan 9, 2026 3:42am
cal-companion Ignored Ignored Preview Jan 9, 2026 3:42am
cal-eu Ignored Ignored Jan 9, 2026 3:42am

…bile

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@pull-request-size pull-request-size bot added size/L and removed size/M labels Jan 8, 2026
@devin-ai-integration devin-ai-integration bot changed the title feat: limit badges to 2 with hover tooltip in UserListTable feat: limit badges to 2 with clickable popover in UserListTable Jan 8, 2026
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@devin-ai-integration devin-ai-integration bot changed the title feat: limit badges to 2 with clickable popover in UserListTable feat: limit badges to 2 with hover/click popover in UserListTable Jan 8, 2026
eunjae-lee and others added 3 commits January 8, 2026 15:44
- Refactor LimitedBadges to concrete component with BadgeItem type
- Move exports to end of files to comply with useExportsLast rule
- Add explicit types to callback parameters for useExplicitType rule
- Convert nested ternary to if-else for filterType calculation
- Remove unused imports (Row, Table types)
- Update ResponseValueCell and UserListTable to use new LimitedBadges API

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
- Add FilterType import from @calcom/types/data-table
- Add FacetedValue import from @calcom/features/data-table
- Type filterType as FilterType to allow reassignment to different ColumnFilterType values
- Type getFacetedUniqueValues return as Map<FacetedValue, number>

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@eunjae-lee
Copy link
Contributor Author

@cubic-dev-ai review this

@cubic-dev-ai
Copy link
Contributor

cubic-dev-ai bot commented Jan 8, 2026

@cubic-dev-ai review this

@eunjae-lee I have started the AI code review. It will take a few minutes to complete.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 3 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web/modules/insights/components/ResponseValueCell.tsx">

<violation number="1" location="apps/web/modules/insights/components/ResponseValueCell.tsx:20">
P2: Potential duplicate React keys if `values` array contains duplicate entries. The original code used the array index `i` in the key to ensure uniqueness (`${cellId}-${i}-${rowId}`), but the new code only uses `${id}-${rowId}`. Include the index to preserve the original uniqueness guarantee.</violation>
</file>

<file name="apps/web/modules/users/components/UserTable/UserListTable.tsx">

<violation number="1" location="apps/web/modules/users/components/UserTable/UserListTable.tsx:263">
P2: Regression: Group option badges lose their distinctive orange styling. The original code used `variant={isAGroupOption ? "orange" : "gray"}` to visually distinguish group options, but this is now hardcoded to `"gray"`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

eunjae-lee and others added 4 commits January 8, 2026 16:39
- Add index to id for unique keys in ResponseValueCell.tsx
- Restore orange variant for group options in UserListTable.tsx
- Fix useMemo dependency array (add t, remove dispatch)
- Fix import ordering with biome
- Convert ternary operators to if-else statements for biome compliance

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
- Remove id field from BadgeItem type in LimitedBadges
- Use label as React key instead of id
- Remove unused rowId parameter from ResponseValueCell
- Update all consumers to not pass id field

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@eunjae-lee eunjae-lee marked this pull request as ready for review January 8, 2026 16:39
@graphite-app graphite-app bot added core area: core, team members only consumer labels Jan 8, 2026
@graphite-app graphite-app bot requested a review from a team January 8, 2026 16:39
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 4 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web/components/ui/LimitedBadges.tsx">

<violation number="1" location="apps/web/components/ui/LimitedBadges.tsx:69">
P2: Using `index` as key instead of `label`. The PR description states the component uses `label` as React key, but the implementation uses `index`. Consider using `item.label` as key to match the documented behavior and avoid potential reconciliation issues when items change.</violation>

<violation number="2" location="apps/web/components/ui/LimitedBadges.tsx:94">
P2: Using `index` as key for hidden items. Consider using `item.label` as key for consistency with the documented behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

<div className="flex flex-col gap-1">
{hiddenItems.map((item, index) => (
<span
key={index}
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 8, 2026

Choose a reason for hiding this comment

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

P2: Using index as key for hidden items. Consider using item.label as key for consistency with the documented behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/components/ui/LimitedBadges.tsx, line 94:

<comment>Using `index` as key for hidden items. Consider using `item.label` as key for consistency with the documented behavior.</comment>

<file context>
@@ -0,0 +1,109 @@
+            <div className="flex flex-col gap-1">
+              {hiddenItems.map((item, index) => (
+                <span
+                  key={index}
+                  className="text-default cursor-pointer text-sm hover:text-emphasis"
+                  onClick={item.onClick}>
</file context>
Suggested change
key={index}
key={item.label}
Fix with Cubic

return (
<div className={`flex flex-wrap items-center gap-x-1 gap-y-1 ${className || ""}`}>
{visibleItems.map((item, index) => (
<Badge key={index} variant={item.variant || "gray"} onClick={item.onClick}>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 8, 2026

Choose a reason for hiding this comment

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

P2: Using index as key instead of label. The PR description states the component uses label as React key, but the implementation uses index. Consider using item.label as key to match the documented behavior and avoid potential reconciliation issues when items change.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/components/ui/LimitedBadges.tsx, line 69:

<comment>Using `index` as key instead of `label`. The PR description states the component uses `label` as React key, but the implementation uses `index`. Consider using `item.label` as key to match the documented behavior and avoid potential reconciliation issues when items change.</comment>

<file context>
@@ -0,0 +1,109 @@
+  return (
+    <div className={`flex flex-wrap items-center gap-x-1 gap-y-1 ${className || ""}`}>
+      {visibleItems.map((item, index) => (
+        <Badge key={index} variant={item.variant || "gray"} onClick={item.onClick}>
+          {item.label}
+        </Badge>
</file context>
Suggested change
<Badge key={index} variant={item.variant || "gray"} onClick={item.onClick}>
<Badge key={item.label} variant={item.variant || "gray"} onClick={item.onClick}>
Fix with Cubic

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

Labels

consumer core area: core, team members only size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants