Skip to content

feat: enhance TextField component with additional className props#118

Closed
lcmohsen wants to merge 1 commit into
mainfrom
fix/pass-classnames-to-suffix-and-prefix-in-textfield
Closed

feat: enhance TextField component with additional className props#118
lcmohsen wants to merge 1 commit into
mainfrom
fix/pass-classnames-to-suffix-and-prefix-in-textfield

Conversation

@lcmohsen
Copy link
Copy Markdown
Contributor

@lcmohsen lcmohsen commented Aug 13, 2025

  • Updated TextInputProps interface to include groupClassName, prefixClassName, and suffixClassName for better customization.
  • Modified TextField component to utilize new className props for FieldPrefix and FieldSuffix, improving styling flexibility.

Summary by CodeRabbit

  • New Features
    • Text field now supports custom class names for the field group, prefix, and suffix, enabling more flexible styling.
    • Label input is now restricted to plain text; update any usage that previously passed non-string content.
  • Chores
    • Bumped components package version to 0.19.2.

@bolt-new-by-stackblitz
Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 13, 2025

Walkthrough

Version bump for components package and updates to TextField props: label type narrowed to string; new optional className props added for group, prefix, and suffix; rendering updated to apply these classes.

Changes

Cohort / File(s) Summary
Components package version
packages/components/package.json
Bump version from 0.19.1 to 0.19.2.
TextField props and styling hooks
packages/components/src/ui/text-field.tsx
Narrow TextInputProps.label to string; add groupClassName, prefixClassName, suffixClassName; update TextField to accept and apply these classes to wrappers and prefix/suffix elements.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

Poem

I twitch my ears at version two,
A tidy hop from .1 to .2.
New coats for prefix, suffix too—
Classy threads in stylish hue.
Label’s trimmed, concise and clean;
Fields now gleam with rabbit sheen. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/pass-classnames-to-suffix-and-prefix-in-textfield

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

- Updated TextInputProps interface to include groupClassName, prefixClassName, and suffixClassName for better customization.
- Modified TextField component to utilize new className props for FieldPrefix and FieldSuffix, improving styling flexibility.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Aug 13, 2025

📝 Storybook Preview: View Storybook

This preview will be updated automatically when you push new changes to this PR.

Note: The preview will be available after the workflow completes and the PR is approved for deployment.

@lcmohsen lcmohsen force-pushed the fix/pass-classnames-to-suffix-and-prefix-in-textfield branch from 7cd6a84 to 1d44933 Compare August 13, 2025 12:55
Copy link
Copy Markdown

@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: 2

🔭 Outside diff range comments (1)
packages/components/src/ui/text-field.tsx (1)

103-114: Preserve consumer-supplied input className by merging props.className.

The explicit className overrides any className provided via {...props}. Merge them to keep consumer overrides.

-                  className={cn('focus-visible:ring-0 focus-visible:ring-offset-0 border-input', {
-                    'rounded-l-none border-l-0': prefix,
-                    'rounded-r-none border-r-0': suffix,
-                  })}
+                  className={cn(
+                    'focus-visible:ring-0 focus-visible:ring-offset-0 border-input',
+                    {
+                      'rounded-l-none border-l-0': prefix,
+                      'rounded-r-none border-r-0': suffix,
+                    },
+                    props.className
+                  )}
🧹 Nitpick comments (1)
packages/components/src/ui/text-field.tsx (1)

100-101: Nit: move always-true classes into the base string for clarity.

These classes are unconditional; keeping them in the object literal with true adds noise.

- className={cn('flex group transition-all duration-200 rounded-md', {
-   'field__input--with-prefix': prefix,
-   'field__input--with-suffix': suffix,
-   'focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background': true
- }, groupClassName)}
+ className={cn(
+   'flex group transition-all duration-200 rounded-md focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background',
+   {
+     'field__input--with-prefix': prefix,
+     'field__input--with-suffix': suffix,
+   },
+   groupClassName
+ )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 461abfe and 1d44933.

📒 Files selected for processing (2)
  • packages/components/package.json (1 hunks)
  • packages/components/src/ui/text-field.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
{package.json,packages/**/package.json,apps/**/package.json}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

Use consistent versioning across packages

Files:

  • packages/components/package.json
{packages/**/package.json,apps/**/package.json}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

Define peerDependencies, dependencies, and devDependencies appropriately in package.json

Files:

  • packages/components/package.json
packages/components/src/ui/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/form-component-patterns.mdc)

packages/components/src/ui/**/*.tsx: Build on @radix-ui components as the foundation for base UI components
Follow the component composition pattern for UI components that accept form integration

Files:

  • packages/components/src/ui/text-field.tsx
packages/components/src/ui/*.{tsx,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/form-component-patterns.mdc)

Base UI components should be named as ComponentName in ui/ directory

Files:

  • packages/components/src/ui/text-field.tsx
**/*.{tsx,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/form-component-patterns.mdc)

**/*.{tsx,ts}: Props interfaces should be named as ComponentNameProps
Form schemas should be named formSchema or componentNameSchema

Files:

  • packages/components/src/ui/text-field.tsx
packages/components/src/{remix-hook-form,ui}/*.{tsx,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/form-component-patterns.mdc)

Always export both the component and its props type

Files:

  • packages/components/src/ui/text-field.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

{apps,packages}/**/*.{ts,tsx}: Use package name imports for published packages (e.g., import { TextField } from '@lambdacurry/forms/remix-hook-form')
Import from specific entry points (e.g., import { TextField } from '@lambdacurry/forms/remix-hook-form/text-field')
Do not use relative imports across packages (e.g., avoid import { TextField } from '../../packages/components/src/remix-hook-form/text-field')
Order imports: 1) external libraries, 2) internal package imports, 3) cross-package imports, 4) type-only imports (grouped separately)

Files:

  • packages/components/src/ui/text-field.tsx
{apps,packages}/**/src/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

{apps,packages}/**/src/**/*.{ts,tsx}: Use relative imports within the same package (e.g., import { FormControl } from './form')
Use relative imports for sibling directories (e.g., import { Button } from '../ui/button')

Files:

  • packages/components/src/ui/text-field.tsx
packages/components/src/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

packages/components/src/**/*.{ts,tsx}: Always export both component and props type (e.g., export { ComponentName }; export type { ComponentNameProps };)
Use named exports for components for better tree-shaking (e.g., export const ComponentName = ...; avoid default exports)
Avoid default exports for components
Use tree-shaking friendly exports

Files:

  • packages/components/src/ui/text-field.tsx
{apps,packages}/**/src/**/*.{tsx,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

{apps,packages}/**/src/**/*.{tsx,ts}: Use kebab-case for component file names (e.g., text-field.tsx, data-table.tsx)
Match component name in PascalCase to the exported component from the file (e.g., text-field.tsx exports TextField)

Files:

  • packages/components/src/ui/text-field.tsx
packages/components/src/ui/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

Don't import from remix-hook-form package in ui components

Files:

  • packages/components/src/ui/text-field.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo-organization.mdc)

Avoid circular dependencies

Files:

  • packages/components/src/ui/text-field.tsx
🧬 Code Graph Analysis (1)
packages/components/src/ui/text-field.tsx (2)
packages/components/src/ui/form.tsx (1)
  • FieldComponents (9-15)
packages/components/src/ui/text-input.tsx (1)
  • InputProps (4-6)
🔇 Additional comments (4)
packages/components/package.json (1)

3-3: Manual verification needed: confirm TextField label usages and forms dependency pins

I wasn’t able to locate any <TextField label={…}> instances passing JSX/ReactNode nor any pinned @lambdacurry/forms versions via automated search. Please manually verify:

• That no consumers use non-string labels on <TextField> (i.e. JSX or ReactNode).
• That all @lambdacurry/forms pins in your package.json files are as expected.

If any JSX/ReactNode labels are in use, narrowing TextField.label to string is a breaking change—either revert to ReactNode or bump to at least 0.20.0 (minor bump under 0.x). Otherwise, the current patch bump to 0.19.2 is sufficient.

packages/components/src/ui/text-field.tsx (3)

65-67: New className props are well-scoped and consistent.

groupClassName/prefixClassName/suffixClassName additions align with customization goals and preserve defaults.


103-103: Prefix className wiring looks good.
Passing prefixClassName through to FieldPrefix achieves the desired customization with no behavioral change.


115-115: Suffix className wiring looks good.
Same as prefix: non-breaking and enables targeted styling.

control?: Control<FieldValues>;
name: FieldPath<FieldValues>;
label?: string | React.ReactNode;
label?: string;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Avoid breaking change: keep label as ReactNode-compatible.

Narrowing label to string breaks valid use cases (icons, formatted labels, i18n components). Recommend restoring React.ReactNode.

Apply:

-  label?: string;
+  label?: React.ReactNode;

If you intend to keep it string-only, please confirm there are no callsites passing JSX. Use the script in package.json review to verify.


🏁 Script executed:

#!/bin/bash
set -e

echo "1) Finding TextField usages with non-string label props (JSX expressions):"
rg -n --type tsx '<TextField[^>]*label\s*=\s*\{' .

echo
echo "2) Finding any label prop passed as an expression anywhere in the repo:"
rg -n --type tsx 'label\s*=\s*\{' .

Length of output: 248


🏁 Script executed:

#!/bin/bash
set -e

echo "Searching for TextField usages with label prop passed as an expression (non-string) in TSX/JSX files:"
rg -n "<TextField[^>]*label\s*=\s*\{" --glob "*.{tsx,jsx}" .

Length of output: 284


Restore ReactNode for the label prop

Our search across all .tsx/.jsx files did not find any instances of non-string labels being passed to <TextField>, so reverting this change is non-breaking today and preserves future flexibility (icons, formatted labels, i18n components).

• File: packages/components/src/ui/text-field.tsx line 57
• Apply:

-  label?: string;
+  label?: React.ReactNode;
🤖 Prompt for AI Agents
In packages/components/src/ui/text-field.tsx around line 57, the label prop was
narrowed to string; restore it to allow ReactNode to preserve flexibility
(icons, formatted labels, i18n). Update the prop type from label?: string to
label?: React.ReactNode (or import ReactNode and use label?: ReactNode), ensure
React is imported or ReactNode is imported from 'react', and keep the prop
optional so existing usage remains valid.

Comment on lines +80 to 83
groupClassName,
prefixClassName,
suffixClassName,
...props
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider forwarding refs properly instead of accepting a ref prop.

Function components don’t receive ref via props; won’t populate the ref variable unless you use React.forwardRef. This is orthogonal to this PR but worth addressing.

Example refactor (outside this hunk):

export const TextField = React.forwardRef<HTMLInputElement, TextInputProps>(function TextField(
  {
    control,
    name,
    label,
    description,
    className,
    components,
    prefix,
    suffix,
    groupClassName,
    prefixClassName,
    suffixClassName,
    ...props
  },
  ref
) {
  // ... use `ref` here
});

Then remove ref from TextInputProps entirely (it will be provided by forwardRef).

🤖 Prompt for AI Agents
In packages/components/src/ui/text-field.tsx around lines 80 to 83, the
component currently expects a ref prop via props which won’t be populated when
consumers use <TextField ref={...}>; change the component to use
React.forwardRef and accept (props, ref) so refs are forwarded correctly, remove
ref from the TextInputProps type, and update internal usage to call the
forwarded ref (or pass it to the underlying input element) instead of reading a
ref from props.

@lcmohsen lcmohsen self-assigned this Aug 13, 2025
@Bfunk54
Copy link
Copy Markdown
Contributor

Bfunk54 commented Aug 13, 2025

I'm not so sure adding this complexity is necessary, if we do need to do apply some styles to these we can use tailwind to target children, especially since we haven't needed to style these up until now. What do you think @jaruesink?

@jaruesink jaruesink closed this Aug 14, 2025
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.

3 participants