Skip to content

fix(tabs): tab cursor#5769

Merged
wingkwong merged 1 commit into
canaryfrom
fix/eng-2785
Oct 4, 2025
Merged

fix(tabs): tab cursor#5769
wingkwong merged 1 commit into
canaryfrom
fix/eng-2785

Conversation

@wingkwong
Copy link
Copy Markdown
Member

@wingkwong wingkwong commented Oct 4, 2025

Closes #5668

📝 Description

⛳️ Current behavior (updates)

🚀 New behavior

💣 Is this a breaking change (Yes/No):

📝 Additional Information

Summary by CodeRabbit

  • Bug Fixes

    • Improved accuracy and consistency of the tab indicator positioning across layouts and responsive sizes.
    • Reduced visual jitter and alignment glitches when switching tabs.
  • Refactor

    • Simplified tab indicator logic to use more reliable element metrics, improving stability and maintainability.

@wingkwong wingkwong added this to the v2.8.5 milestone Oct 4, 2025
@wingkwong wingkwong requested a review from jrgarciadev as a code owner October 4, 2025 08:51
@linear
Copy link
Copy Markdown

linear Bot commented Oct 4, 2025

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Oct 4, 2025

⚠️ No Changeset found

Latest commit: c8a0cc4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel
Copy link
Copy Markdown

vercel Bot commented Oct 4, 2025

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

Project Deployment Preview Comments Updated (UTC)
heroui Ready Ready Preview Comment Oct 4, 2025 9:05am
heroui-sb Ready Ready Preview Comment Oct 4, 2025 9:05am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 4, 2025

Walkthrough

Removed "bordered" variant handling and parent DOMRect dependency from tabs cursor logic; cursor positioning now derives directly from the selected tab’s offsetWidth/offsetHeight and container-relative left/top, and related ref/caller code simplified.

Changes

Cohort / File(s) Summary
Tabs cursor positioning refactor
packages/components/tabs/src/tabs.tsx
- Removed "bordered" variant logic from getCursorStyles
- updateCursorPosition now reads selectedTab.offsetWidth / offsetHeight and uses container-relative left/top from the selected tab
- handleCursorRef simplified to call updateCursorPosition(node, selectedTab) (removed parentRect computation)
- Call sites updated to drop the parentRect argument

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant T as Tabs component
  participant ST as selectedTab (DOM)
  participant C as Cursor (DOM)
  participant G as getCursorStyles

  U->>T: Select tab / Mount
  T->>ST: Read offsetWidth, offsetHeight, offsetTop, bounding rect (container)
  T->>G: getCursorStyles({width, height, left: container-relative, top: offsetTop, variant})
  G-->>T: style object (no "bordered" branch)
  T->>C: Apply style (left/top/width/height)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • jrgarciadev
  • winchesHe

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The changes refactor cursor styling and positioning logic in the Tabs component but do not address the linked issue’s bug of the input becoming disabled or readonly when interacting with tabs inside a popover. Implement or verify focus handling and event propagation so that interacting with the Tabs does not disable the input in the popover as described in issue #5668.
Out of Scope Changes Check ⚠️ Warning The removal of the “bordered” variant handling and unrelated simplification of cursor update logic appear unrelated to the bug report’s objective of preserving input focus in a popover. Remove or isolate unrelated style and variant changes from this pull request to focus solely on the bug fix for input focus in the popover scenario.
Description Check ⚠️ Warning The pull request description includes the template structure and closes the issue but fails to provide the required brief description, current behavior, new behavior, and breaking change details, leaving all template sections empty. Please fill in the “Description”, “Current behavior (updates)”, “New behavior”, and “Is this a breaking change” sections with concrete details of what was changed, why, and any potential impact.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely indicates that this pull request fixes the tab cursor in the Tabs component, matching the primary change to cursor style and positioning without extraneous details.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/eng-2785

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.

Copy link
Copy Markdown
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

🧹 Nitpick comments (1)
packages/components/tabs/src/tabs.tsx (1)

76-88: Simplification looks good, but consider a type-safe refactor.

The refactored approach using native offset properties is cleaner and likely resolves the popover focus issue. However, the type assertion creating a partial DOMRect (lines 77-80) is a code smell.

Consider this type-safe alternative that avoids the assertion:

-  const updateCursorPosition = (node: HTMLSpanElement, selectedTab: HTMLElement) => {
-    const tabRect = {
-      width: selectedTab.offsetWidth,
-      height: selectedTab.offsetHeight,
-    } as DOMRect;
-
-    const styles = getCursorStyles(tabRect, selectedTab.offsetLeft, selectedTab.offsetTop);
+  const updateCursorPosition = (node: HTMLSpanElement, selectedTab: HTMLElement) => {
+    const tabRect: Pick<DOMRect, 'width' | 'height'> = {
+      width: selectedTab.offsetWidth,
+      height: selectedTab.offsetHeight,
+    };
+
+    const styles = getCursorStyles(tabRect as DOMRect, selectedTab.offsetLeft, selectedTab.offsetTop);

Or refactor getCursorStyles to accept width/height directly instead of DOMRect:

const getCursorStyles = (width: number, height: number, relativeLeft: number, relativeTop: number) => {
  const baseStyles = {
    left: `${relativeLeft}px`,
    width: `${width}px`,
  };

  if (variant === "underlined") {
    return {
      left: `${relativeLeft + width * 0.1}px`,
      top: `${relativeTop + height - 2}px`,
      width: `${width * 0.8}px`,
      height: "",
    };
  }

  return {
    ...baseStyles,
    top: `${relativeTop}px`,
    height: `${height}px`,
  };
};
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f17962 and c8a0cc4.

📒 Files selected for processing (1)
  • packages/components/tabs/src/tabs.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Continuous Release
  • GitHub Check: Prettier
  • GitHub Check: ESLint
  • GitHub Check: Build
  • GitHub Check: Tests
  • GitHub Check: TypeScript

Comment thread packages/components/tabs/src/tabs.tsx
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Oct 4, 2025

Open in StackBlitz

@heroui/accordion

npm i https://pkg.pr.new/@heroui/accordion@5769

@heroui/alert

npm i https://pkg.pr.new/@heroui/alert@5769

@heroui/autocomplete

npm i https://pkg.pr.new/@heroui/autocomplete@5769

@heroui/avatar

npm i https://pkg.pr.new/@heroui/avatar@5769

@heroui/badge

npm i https://pkg.pr.new/@heroui/badge@5769

@heroui/breadcrumbs

npm i https://pkg.pr.new/@heroui/breadcrumbs@5769

@heroui/button

npm i https://pkg.pr.new/@heroui/button@5769

@heroui/calendar

npm i https://pkg.pr.new/@heroui/calendar@5769

@heroui/card

npm i https://pkg.pr.new/@heroui/card@5769

@heroui/checkbox

npm i https://pkg.pr.new/@heroui/checkbox@5769

@heroui/chip

npm i https://pkg.pr.new/@heroui/chip@5769

@heroui/code

npm i https://pkg.pr.new/@heroui/code@5769

@heroui/date-input

npm i https://pkg.pr.new/@heroui/date-input@5769

@heroui/date-picker

npm i https://pkg.pr.new/@heroui/date-picker@5769

@heroui/divider

npm i https://pkg.pr.new/@heroui/divider@5769

@heroui/drawer

npm i https://pkg.pr.new/@heroui/drawer@5769

@heroui/dropdown

npm i https://pkg.pr.new/@heroui/dropdown@5769

@heroui/form

npm i https://pkg.pr.new/@heroui/form@5769

@heroui/image

npm i https://pkg.pr.new/@heroui/image@5769

@heroui/input

npm i https://pkg.pr.new/@heroui/input@5769

@heroui/input-otp

npm i https://pkg.pr.new/@heroui/input-otp@5769

@heroui/kbd

npm i https://pkg.pr.new/@heroui/kbd@5769

@heroui/link

npm i https://pkg.pr.new/@heroui/link@5769

@heroui/listbox

npm i https://pkg.pr.new/@heroui/listbox@5769

@heroui/menu

npm i https://pkg.pr.new/@heroui/menu@5769

@heroui/modal

npm i https://pkg.pr.new/@heroui/modal@5769

@heroui/navbar

npm i https://pkg.pr.new/@heroui/navbar@5769

@heroui/number-input

npm i https://pkg.pr.new/@heroui/number-input@5769

@heroui/pagination

npm i https://pkg.pr.new/@heroui/pagination@5769

@heroui/popover

npm i https://pkg.pr.new/@heroui/popover@5769

@heroui/progress

npm i https://pkg.pr.new/@heroui/progress@5769

@heroui/radio

npm i https://pkg.pr.new/@heroui/radio@5769

@heroui/ripple

npm i https://pkg.pr.new/@heroui/ripple@5769

@heroui/scroll-shadow

npm i https://pkg.pr.new/@heroui/scroll-shadow@5769

@heroui/select

npm i https://pkg.pr.new/@heroui/select@5769

@heroui/skeleton

npm i https://pkg.pr.new/@heroui/skeleton@5769

@heroui/slider

npm i https://pkg.pr.new/@heroui/slider@5769

@heroui/snippet

npm i https://pkg.pr.new/@heroui/snippet@5769

@heroui/spacer

npm i https://pkg.pr.new/@heroui/spacer@5769

@heroui/spinner

npm i https://pkg.pr.new/@heroui/spinner@5769

@heroui/switch

npm i https://pkg.pr.new/@heroui/switch@5769

@heroui/table

npm i https://pkg.pr.new/@heroui/table@5769

@heroui/tabs

npm i https://pkg.pr.new/@heroui/tabs@5769

@heroui/toast

npm i https://pkg.pr.new/@heroui/toast@5769

@heroui/tooltip

npm i https://pkg.pr.new/@heroui/tooltip@5769

@heroui/user

npm i https://pkg.pr.new/@heroui/user@5769

@heroui/react

npm i https://pkg.pr.new/@heroui/react@5769

@heroui/system

npm i https://pkg.pr.new/@heroui/system@5769

@heroui/system-rsc

npm i https://pkg.pr.new/@heroui/system-rsc@5769

@heroui/theme

npm i https://pkg.pr.new/@heroui/theme@5769

@heroui/aria-utils

npm i https://pkg.pr.new/@heroui/aria-utils@5769

@heroui/dom-animation

npm i https://pkg.pr.new/@heroui/dom-animation@5769

@heroui/framer-utils

npm i https://pkg.pr.new/@heroui/framer-utils@5769

@heroui/react-rsc-utils

npm i https://pkg.pr.new/@heroui/react-rsc-utils@5769

@heroui/react-utils

npm i https://pkg.pr.new/@heroui/react-utils@5769

@heroui/shared-icons

npm i https://pkg.pr.new/@heroui/shared-icons@5769

@heroui/shared-utils

npm i https://pkg.pr.new/@heroui/shared-utils@5769

@heroui/stories-utils

npm i https://pkg.pr.new/@heroui/stories-utils@5769

@heroui/test-utils

npm i https://pkg.pr.new/@heroui/test-utils@5769

@heroui/use-aria-accordion

npm i https://pkg.pr.new/@heroui/use-aria-accordion@5769

@heroui/use-aria-accordion-item

npm i https://pkg.pr.new/@heroui/use-aria-accordion-item@5769

@heroui/use-aria-button

npm i https://pkg.pr.new/@heroui/use-aria-button@5769

@heroui/use-aria-link

npm i https://pkg.pr.new/@heroui/use-aria-link@5769

@heroui/use-aria-modal-overlay

npm i https://pkg.pr.new/@heroui/use-aria-modal-overlay@5769

@heroui/use-aria-multiselect

npm i https://pkg.pr.new/@heroui/use-aria-multiselect@5769

@heroui/use-aria-overlay

npm i https://pkg.pr.new/@heroui/use-aria-overlay@5769

@heroui/use-callback-ref

npm i https://pkg.pr.new/@heroui/use-callback-ref@5769

@heroui/use-clipboard

npm i https://pkg.pr.new/@heroui/use-clipboard@5769

@heroui/use-data-scroll-overflow

npm i https://pkg.pr.new/@heroui/use-data-scroll-overflow@5769

@heroui/use-disclosure

npm i https://pkg.pr.new/@heroui/use-disclosure@5769

@heroui/use-draggable

npm i https://pkg.pr.new/@heroui/use-draggable@5769

@heroui/use-form-reset

npm i https://pkg.pr.new/@heroui/use-form-reset@5769

@heroui/use-image

npm i https://pkg.pr.new/@heroui/use-image@5769

@heroui/use-infinite-scroll

npm i https://pkg.pr.new/@heroui/use-infinite-scroll@5769

@heroui/use-intersection-observer

npm i https://pkg.pr.new/@heroui/use-intersection-observer@5769

@heroui/use-is-mobile

npm i https://pkg.pr.new/@heroui/use-is-mobile@5769

@heroui/use-is-mounted

npm i https://pkg.pr.new/@heroui/use-is-mounted@5769

@heroui/use-measure

npm i https://pkg.pr.new/@heroui/use-measure@5769

@heroui/use-pagination

npm i https://pkg.pr.new/@heroui/use-pagination@5769

@heroui/use-real-shape

npm i https://pkg.pr.new/@heroui/use-real-shape@5769

@heroui/use-ref-state

npm i https://pkg.pr.new/@heroui/use-ref-state@5769

@heroui/use-resize

npm i https://pkg.pr.new/@heroui/use-resize@5769

@heroui/use-safe-layout-effect

npm i https://pkg.pr.new/@heroui/use-safe-layout-effect@5769

@heroui/use-scroll-position

npm i https://pkg.pr.new/@heroui/use-scroll-position@5769

@heroui/use-ssr

npm i https://pkg.pr.new/@heroui/use-ssr@5769

@heroui/use-theme

npm i https://pkg.pr.new/@heroui/use-theme@5769

@heroui/use-update-effect

npm i https://pkg.pr.new/@heroui/use-update-effect@5769

@heroui/use-viewport-size

npm i https://pkg.pr.new/@heroui/use-viewport-size@5769

commit: c8a0cc4

Copy link
Copy Markdown
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

🧹 Nitpick comments (1)
packages/components/tabs/src/tabs.tsx (1)

77-80: Use a more precise type for the partial DOMRect.

The object literal is cast as DOMRect but only contains width and height. Since getCursorStyles only uses these two properties, consider using a more precise interface or inline type to avoid misleading type assertions:

- const tabRect = {
-   width: selectedTab.offsetWidth,
-   height: selectedTab.offsetHeight,
- } as DOMRect;
+ const tabRect: Pick<DOMRect, 'width' | 'height'> = {
+   width: selectedTab.offsetWidth,
+   height: selectedTab.offsetHeight,
+ };

Or update getCursorStyles signature to accept Pick<DOMRect, 'width' | 'height'> instead of DOMRect.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8a0cc4 and 9375dd3.

📒 Files selected for processing (1)
  • packages/components/tabs/src/tabs.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: TypeScript
  • GitHub Check: Continuous Release
  • GitHub Check: Prettier
  • GitHub Check: ESLint
  • GitHub Check: Tests
  • GitHub Check: Build
🔇 Additional comments (2)
packages/components/tabs/src/tabs.tsx (2)

110-110: LGTM: Simplified call aligns with the refactored signature.

The removal of the parentRect argument simplifies the call and matches the updated updateCursorPosition signature.


76-111: Verify popover input bug fix: Confirm that the new cursor-positioning logic resolves the issue where Inputs become disabled/read-only when Tabs live inside Popovers (issue #5668). Reproduce via https://github.com/kiinoo/bug4344.git and test edge cases: horizontal/vertical scrolling, nested popovers, and CSS transforms.

Comment thread packages/components/tabs/src/tabs.tsx Outdated
@wingkwong wingkwong merged commit 812f793 into canary Oct 4, 2025
16 checks passed
@wingkwong wingkwong deleted the fix/eng-2785 branch October 4, 2025 09:42
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.

[BUG] - Popover with Tabs in it makes Input disabled

1 participant