Skip to content

Conversation

hassanzadeh-mj
Copy link

@hassanzadeh-mj hassanzadeh-mj commented Oct 11, 2025

…outside placement

  • Fix label positioning issue in Select, Input, and NumberInput components
  • Label now maintains stable position when error message appears/disappears
  • Wrapped trigger/input element in relative positioned container
  • Error messages are now outside the label's positioning context

Fixes #5796

Closes #

📝 Description

⛳️ Current behavior (updates)

🚀 New behavior

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

📝 Additional Information

Summary by CodeRabbit

  • Style
    • Fixed label positioning for Input, NumberInput, and Select when the label is rendered outside the field to prevent shifts when helper/error messages appear.
    • Added a stable relative wrapper around field controls to ensure consistent alignment and stacking with triggers, values, indicators, and popovers.
    • Preserves existing label, helper, description, and error behavior; no visual change when labels are inside.

…outside placement

- Fix label positioning issue in Select, Input, and NumberInput components
- Label now maintains stable position when error message appears/disappears
- Wrapped trigger/input element in relative positioned container
- Error messages are now outside the label's positioning context

Fixes heroui-inc#5796
Copy link

changeset-bot bot commented Oct 11, 2025

🦋 Changeset detected

Latest commit: e993a6a

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

This PR includes changesets to release 5 packages
Name Type
@heroui/input Patch
@heroui/number-input Patch
@heroui/select Patch
@heroui/autocomplete Patch
@heroui/react 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

Copy link

vercel bot commented Oct 11, 2025

@hassanzadeh-mj is attempting to deploy a commit to the HeroUI Inc Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Oct 11, 2025

Walkthrough

Adds an additional positioned wrapper (style: position: relative) around the existing input/trigger wrapper when shouldLabelBeOutside is true for Input, NumberInput, and Select. Select also adjusts label render order when the label is outside. No public API or exported declarations changed.

Changes

Cohort / File(s) Summary of changes
Form controls — outside-label wrapper
packages/components/input/src/input.tsx, packages/components/number-input/src/number-input.tsx, packages/components/select/src/select.tsx
When shouldLabelBeOutside is true, wrap the existing input/trigger wrapper in an extra div with style: { position: 'relative' }. Preserve inner wrapper content, helper/description/error logic; Select moves conditional label rendering to precede the Trigger under the outside-label case. No API signature changes.
Changeset
.changeset/fix-label-position-outside.md
Adds a patch changeset for @heroui/input, @heroui/number-input, and @heroui/select describing the fix for label position shifts (issue #5796) and the introduced relative wrappers.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Component as Input/NumberInput/Select
  participant DOM as Render Tree

  User->>Component: Render(props with shouldLabelBeOutside)
  alt shouldLabelBeOutside = true
    Component->>DOM: Create outer div (position: relative)
    Component->>DOM: Render Label (outside / before inner wrapper)
    Component->>DOM: Render inner wrapper (input/trigger + contents)
    Component->>DOM: Render helper/description/error (outside label's positioning context)
  else shouldLabelBeOutside = false
    Component->>DOM: Render wrapper (input/trigger + inline Label)
    Component->>DOM: Render helper/description/error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • jrgarciadev
  • wingkwong

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description fails to follow the repository template: it leaves all template sections empty, retains the placeholder “Closes #”, and instead uses generic bullet points rather than populating the required Description, Current behavior, New behavior, and Breaking change sections. Please fill out the template by linking the issue under “Closes #5796,” providing a brief summary under Description, detailing the current and new behaviors, and indicating whether this is a breaking change.
Title Check ❓ Inconclusive The title uses an ellipsis and is truncated, so it is unclear what specific context “…” refers to and the summary feels incomplete even though it mentions preventing label shift when an error message appears. Please remove the ellipsis and complete the title to clearly specify the context and the nature of the fix, for example by stating “prevent label shift when error message appears with outside placement.”
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed The changes wrap the Input, NumberInput, and Select components in a relatively positioned container to isolate label absolute positioning from the error message layout, directly addressing the objectives of issue #5796 by preventing label shifts regardless of error height.
Out of Scope Changes Check ✅ Passed All modifications are scoped to adding a relative wrapper around form controls in Input, NumberInput, and Select components to fix label positioning; there are no unrelated or extraneous changes.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent 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 e52a78f and e993a6a.

📒 Files selected for processing (1)
  • .changeset/fix-label-position-outside.md (1 hunks)

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
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: 0

♻️ Duplicate comments (2)
packages/components/number-input/src/number-input.tsx (1)

108-113: LGTM! Code duplication detected.

The relative-positioned wrapper correctly implements the label positioning fix. The structure matches the pattern in Input component.

This is the same wrapper pattern duplicated from Input (lines 87-92). See the comment on input.tsx for a suggested refactor to extract this common pattern into a reusable utility component.

packages/components/select/src/select.tsx (1)

158-172: LGTM! Same duplication concern as other components.

The relative-positioned wrapper correctly isolates the trigger's positioning context from error messages. The label rendering logic appropriately places the label before the trigger when outside, and inside when not.

Like Input and NumberInput, this pattern could benefit from extraction into a shared utility. See the comment on input.tsx for a suggested approach.

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

87-92: LGTM! Consider extracting the common wrapper pattern.

The relative-positioned wrapper correctly isolates the label's positioning context from the error message, achieving the PR objective. The nested structure ensures the label uses the wrapper as its positioning ancestor rather than a container that includes the error message.

However, this exact pattern is duplicated in NumberInput and Select components. Consider extracting a reusable wrapper component or utility function to reduce duplication and improve maintainability.

Example extraction:

// shared utility component
const LabelPositioningWrapper = ({ 
  children, 
  shouldWrap 
}: { 
  children: React.ReactNode; 
  shouldWrap: boolean;
}) => {
  if (shouldWrap) {
    return <div style={{position: "relative"}}>{children}</div>;
  }
  return <>{children}</>;
};

Then use it consistently across Input, NumberInput, and Select:

 if (shouldLabelBeOutside) {
   return (
     <div {...getMainWrapperProps()}>
-      <div style={{position: "relative"}}>
+      <LabelPositioningWrapper shouldWrap>
         <div {...getInputWrapperProps()}>
           {!isOutsideLeft && !isOutsideTop ? labelContent : null}
           {innerWrapper}
         </div>
-      </div>
+      </LabelPositioningWrapper>
       {helperWrapper}
     </div>
   );
 }
📜 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 3737870 and e52a78f.

📒 Files selected for processing (3)
  • packages/components/input/src/input.tsx (1 hunks)
  • packages/components/number-input/src/number-input.tsx (1 hunks)
  • packages/components/select/src/select.tsx (1 hunks)
🔇 Additional comments (1)
packages/components/select/src/select.tsx (1)

159-161: Verify label positioning for all labelPlacement values.
Confirm that shouldLabelBeOutside is true for every outside placement (top, top-start, top-end, outside, outside-left) and that the updated DOM order doesn’t break the visual layout or the label’s absolute‐positioning CSS.

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] - Label position is affected by the height of the error message

1 participant