Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 16 additions & 25 deletions libs/ui/src/molecules/form-input.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { ReactNode } from "react"
import { ErrorText } from "../atoms/error-text"
import { ExtraText } from "../atoms/extra-text"
import { Input, type InputProps } from "../atoms/input"
import { Label } from "../atoms/label"
import { StatusText } from "../atoms/status-text"
Comment thread
BleedingDev marked this conversation as resolved.
Resolved

type ValidateStatus = "default" | "error" | "success" | "warning"

Expand All @@ -11,22 +10,18 @@ interface FormInputRawProps extends InputProps {
label: ReactNode
validateStatus?: ValidateStatus
helpText?: ReactNode
extraText?: ReactNode
}

export function FormInputRaw({
id,
label,
validateStatus = "default",
helpText,
extraText,
size = "md",
required,
disabled,
...props
}: FormInputRawProps) {
const extraTextId = extraText ? `${id}-extra` : undefined

return (
<div className="flex flex-col gap-form-field-gap">
<Label disabled={disabled} htmlFor={id} required={required} size={size}>
Expand All @@ -42,38 +37,34 @@ export function FormInputRaw({
className="p-input-sm md:p-input-md"
/>

{/* Status message */}
{helpText}

{extraText && (
<ExtraText id={extraTextId} size={size}>
{extraText}
</ExtraText>
)}
</div>
)
}

type FormInputProps = FormInputRawProps & {
showHelpTextIcon?: boolean
}

export function FormInput({
helpText,
id,
validateStatus,
size,
validateStatus = "default",
showHelpTextIcon = validateStatus !== "default",
size = "md",
...props
}: FormInputRawProps) {
const helpTextId = helpText ? `${id}-helper` : undefined

}: FormInputProps) {
return (
<FormInputRaw
helpText={
validateStatus === "error" ? (
<ErrorText id={helpTextId} showIcon size={size}>
{helpText}
</ErrorText>
) : (
<ExtraText id={helpTextId} size={size}>
helpText && (
<StatusText
status={validateStatus}
showIcon={showHelpTextIcon}
size={size}
>
{helpText}
</ExtraText>
</StatusText>
)
}
id={id}
Expand Down
87 changes: 82 additions & 5 deletions libs/ui/stories/molecules/form-input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const meta: Meta<typeof FormInput> = {
options: ['default', 'error', 'success', 'warning'],
description: 'Validation state that affects input style and helper text',
},
showHelpTextIcon: {
control: 'boolean',
description: 'Whether to show an icon with the help text',
},
},
}

Expand Down Expand Up @@ -70,6 +74,15 @@ export const AllVariants: Story = {
</VariantGroup>

<VariantGroup title="Validation States">
<div className="w-64">
<FormInput
id="default-input"
label="Default state"
placeholder="Enter value"
validateStatus="default"
helpText="This is default help text"
/>
</div>
<div className="w-64">
<FormInput
id="success-input"
Expand All @@ -81,7 +94,7 @@ export const AllVariants: Story = {
</div>
<div className="w-64">
<FormInput
id="success-input"
id="error-input"
label="Error state"
placeholder="Enter email"
validateStatus="error"
Expand All @@ -90,7 +103,7 @@ export const AllVariants: Story = {
</div>
<div className="w-64">
<FormInput
id="success-input"
id="warning-input"
label="Warning state"
placeholder="Enter password"
validateStatus="warning"
Expand All @@ -111,10 +124,9 @@ export const AllVariants: Story = {
<div className="w-64">
<FormInput
id="error-input"
label="With helper and extra text"
label="With error message"
placeholder="Enter value"
helpText="This is error text!"
extraText="This is extra text below input"
validateStatus="error"
/>
</div>
Expand Down Expand Up @@ -183,6 +195,71 @@ export const AllVariants: Story = {
),
}

// Validation States - Dedicated story showing all 4 validation states
export const ValidationStates: Story = {
render: () => (
<div className="flex flex-col gap-4 w-64">
<FormInput
id="default-validation"
label="Default State"
placeholder="Enter username"
validateStatus="default"
helpText="This is default help text"
/>
<FormInput
id="error-validation"
label="Error State"
placeholder="Enter username"
validateStatus="error"
helpText="Username is already taken"
/>
<FormInput
id="success-validation"
label="Success State"
placeholder="Enter username"
validateStatus="success"
helpText="Username is available"
/>
<FormInput
id="warning-validation"
label="Warning State"
placeholder="Enter username"
validateStatus="warning"
helpText="Username contains special characters"
/>
</div>
),
}

// Sizes - Dedicated story showing all size variants
export const Sizes: Story = {
render: () => (
<div className="flex flex-col gap-4 w-64">
<FormInput
id="small-size"
label="Small Size"
placeholder="Enter value"
size="sm"
helpText="Small input variant"
/>
<FormInput
id="medium-size"
label="Medium Size (Default)"
placeholder="Enter value"
size="md"
helpText="Medium input variant"
/>
<FormInput
id="large-size"
label="Large Size"
placeholder="Enter value"
size="lg"
helpText="Large input variant"
/>
</div>
),
}

// Interactive validation example
export const InteractiveValidation: Story = {
render: () => {
Expand Down Expand Up @@ -262,7 +339,7 @@ export const RegistrationForm: Story = {
label="Username"
placeholder="johndoe"
required
extraText="Visible to other users"
helpText="Visible to other users"
/>

<FormInput
Expand Down
Loading