Skip to content

Conversation

@swayamk05
Copy link

@swayamk05 swayamk05 commented Dec 23, 2025

Description

Fixes #91

This PR implements three new form components (Select, TextInput, Textarea) to align with the library's design system standards. These components are fully typed, interactive, and include comprehensive test coverage.

Changes

1. Select Component

  • Built using Headless UI (Listbox) to ensure full accessibility and keyboard navigation.
  • Supports custom size (sm, md, lg, xl) and variant (subtle, outline, ghost) props.
  • Includes support for prefix icons and disabled options.

2. TextInput Component

  • Added support for label, description, and error message rendering.
  • Implemented prefix and suffix slots for icons/text.
  • specific prop typing to prevent "implicit any" errors.

3. Textarea Component

  • Added rows control along with standard form props (label, error, description).
  • Standardized styling to match TextInput for consistent UI.

4. Quality Assurance

  • Unit Tests: Added Jest/Vitest tests for all three components verifying rendering, interaction, and error states.
  • Storybook: Added stories for all variants (Default, With Icons, With Error, etc.).
  • Types: strict TypeScript definitions added to types.ts files.

Verification

I have verified the changes locally:

  • npm run test:unit passed successfully for all new components.
  • npm run lint:js and npm run lint:types passed.
  • Validated visual states in Storybook.

Checklist

  • My branch is targeted at main.
  • I have included tests to confirm the new behavior.
  • I have updated TypeScript declarations.
  • The code follows the project's coding style (ran npm run lint:js:fix).

@b1ink0 b1ink0 changed the base branch from main to develop December 24, 2025 06:53
@swayamk05
Copy link
Author

Apologies! I realized the package-lock.json was needed. I've restored it now.

Comment on lines 11 to 16
label: { control: "text" },
placeholder: { control: "text" },
disabled: { control: "boolean" },
variant: { control: "select", options: ["outline", "subtle"] },
size: { control: "select", options: ["sm", "md", "lg", "xl"] },
rows: { control: "number" },
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please include a descriptions as well for the argTypes properties.

value: string;
label: string;
disabled?: boolean;
[key: string]: any; // Allow extra props
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need this type? We should avoid using overly general types as much as possible.

variant?: SelectVariant;
disabled?: boolean;
value?: string;
value?: SelectOption; // Changed from string to object for Headless UI
Copy link
Collaborator

Choose a reason for hiding this comment

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

General feedback for all the code: remove any unnecessary or overly specific comments. Comments should only be added when the code is not self-explanatory.

Comment on lines 11 to 13
// 1. Define valid keys for casting
type SizeKey = "sm" | "md" | "lg" | "xl";
type VariantKey = "subtle" | "outline" | "ghost";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Types should be located in the types.ts file.

Comment on lines 52 to 54
// --- Styles ---
// Fix 1: Cast 'size' to SizeKey
const sizeClasses = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

style?: Record<string, string | number | boolean>;
}
style?: CSSProperties; // Improved type
[key: string]: any; // Allow other standard input props (onBlur, onFocus, etc.)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of allowing any types, please extend prop type with a proper type such as React.InputHTMLAttributes<HTMLInputElement>.

error?: string; // Added
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
prefix?: ReactNode; // Simplified type
suffix?: ReactNode; // Simplified type
Copy link
Collaborator

Choose a reason for hiding this comment

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

import type { ReactNode, ChangeEvent, CSSProperties } from "react";
// Remove "../common/types" if it causes issues, otherwise keep it.
// Assuming TextInputTypes is simple string union for now to be safe.
export type TextInputType = "text" | "number" | "email" | "password" | "search" | "tel" | "url" | "date" | "datetime-local" | "time";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Try using the common types first. If that doesn’t work, we can use a custom type here.

@b1ink0
Copy link
Collaborator

b1ink0 commented Dec 24, 2025

Apologies! I realized the package-lock.json was needed. I've restored it now.

Thank You!

@swayamk05 I’ve added some initial feedback please make sure to apply it consistently across all the changes in this PR.

@swayamk05 swayamk05 requested a review from b1ink0 December 25, 2025 11:00
@swayamk05
Copy link
Author

Thanks for the detailed review! I've updated the types to be strict (removed any), fixed the Headless UI deprecations, and added full descriptions to the Storybook argTypes. Everything should be good to go now.

@b1ink0
Copy link
Collaborator

b1ink0 commented Dec 26, 2025

@swayamk05, could you please align the styles and subtypes of the components with the referenced Espresso by Frappe Figma design for the Select, TextInput, and TextArea components?

For example, in TextArea, the underline and ghost variants are missing, and only sm, md, and lg sizes should be there. There are also different styles for various states such as success, warning, error, and loading. Also check other component styles as well.

@swayamk05
Copy link
Author

@b1ink0 Aligned Select, TextArea, and TextInput with the Espresso Design System. Added missing ghost/underline variants and success/warning/error states.


<div className="relative flex items-center">
{prefix && (
<div className={`absolute left-${iconPos} flex items-center text-gray-700 dark:text-gray-400 pointer-events-none`}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Tailwind classes should never be dynamic, as these classes are generated at build time. You need to follow the same pattern used above conditionally applying the required classes using clsx. Please fix this throughout the changed files.

Comment on lines +171 to +173
// FIX: Added 'relative z-0' to define stacking context
// FIX: Kept 'resize-none' and dark placeholder colors
"w-full block outline-none appearance-none transition-colors resize-none placeholder:text-gray-800 dark:placeholder:text-gray-500 relative z-0",
Copy link
Collaborator

@b1ink0 b1ink0 Dec 30, 2025

Choose a reason for hiding this comment

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

Please remove these unnecessary comments. And also from throughout the changed files.

Comment on lines +86 to +94
export const AllOutline = () => (
<div className="space-y-6 w-72 p-6 border rounded bg-white dark:bg-gray-900 dark:border-gray-700">
<h3 className="text-sm font-bold text-gray-900 dark:text-white uppercase">Outline Variants</h3>
<Textarea variant="outline" placeholder="Default" />
<Textarea variant="outline" state="success" placeholder="Success" value="Success State" />
<Textarea variant="outline" state="warning" placeholder="Warning" value="Warning State" />
<Textarea variant="outline" state="error" placeholder="Error" value="Error State" />
</div>
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don’t need to show all variants at the same time. It should display only the Outline variant by default, and use the Storybook args and render function pattern so users can change the variant from the Storybook controls panel.

We also don’t need the wrapper with a border around the components it doesn’t look correct for component demo in Storybook. Please look at how other components (like the Button component stories) handle this and apply the same approach.

Apply this feedback to all the stories added in this PR.

Comment on lines +56 to +60
const sizeClasses = {
sm: "text-sm h-7",
md: "text-base h-8",
lg: "text-lg h-10",
}[size as SizeKey];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Comment on lines +106 to +118
// 4. UNDERLINE
if (variant === "underline") {
switch (state) {
case "error":
return "bg-transparent border-b border-red-500 text-red-800 rounded-none px-0 hover:border-red-600 focus:ring-0 focus:border-red-600 dark:text-red-400 dark:border-red-500";
case "success":
return "bg-transparent border-b border-green-500 text-green-800 rounded-none px-0 hover:border-green-600 focus:ring-0 focus:border-green-600 dark:text-green-400 dark:border-green-500";
case "warning":
return "bg-transparent border-b border-orange-500 text-orange-800 rounded-none px-0 hover:border-orange-600 focus:ring-0 focus:border-orange-600 dark:text-orange-400 dark:border-orange-500";
default:
return "bg-transparent border-b border-gray-300 text-gray-900 rounded-none px-0 hover:border-gray-400 focus:ring-0 focus:border-gray-900 dark:text-white dark:border-gray-700 dark:hover:border-gray-500";
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don’t use switch statements pattern. Follow the same pattern used for handling variants in the Button component and apply that approach consistently.

Please apply this feedback to all components added in this PR.

@b1ink0
Copy link
Collaborator

b1ink0 commented Dec 30, 2025

@swayamk05 Added more feedback!

@mohdsayed mohdsayed changed the title Feat/add form components Feature: Add form components Dec 31, 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