Skip to content

Commit

Permalink
refactor: update documentation and examples for form components
Browse files Browse the repository at this point in the history
  • Loading branch information
iatopilskii committed Dec 4, 2024
1 parent 1cc87a0 commit ed675cd
Show file tree
Hide file tree
Showing 12 changed files with 25 additions and 128 deletions.
7 changes: 2 additions & 5 deletions packages/ui/src/components/form/caption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ interface CaptionProps extends PropsWithChildren {
/**
* Caption component that renders supplementary text below form inputs.
* Used to provide additional context or hints for form fields.
*
* @param {CaptionProps} props - The properties for the Caption component.
* @param {React.ReactNode} props.children - The content to be displayed as the caption text.
* @param {string} [props.className] - Optional additional class names for styling.
* @returns {JSX.Element} The rendered Caption component.
* @example
* <Caption>This is a caption</Caption>
*/
export function Caption({ children, className }: CaptionProps) {
return (
Expand Down
9 changes: 3 additions & 6 deletions packages/ui/src/components/form/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ interface CheckboxProps extends ComponentPropsWithoutRef<typeof CheckboxPrimitiv
/**
* Checkbox component that provides a customizable, accessible checkbox input.
* Built on top of Radix UI Checkbox primitive with additional styling.
*
* @param {CheckboxProps} props - The properties for the Checkbox component.
* @param {string} [props.className] - Optional additional class names for styling.
* @param {React.Ref<HTMLButtonElement>} ref - Forward ref for the checkbox element.
* @returns {JSX.Element} The rendered Checkbox component.
* @example
* <Checkbox id="checkbox" checked={checkboxValue} onCheckedChange={checkboxChange} />
*/
const Checkbox = forwardRef<ElementRef<typeof CheckboxPrimitive.Root>, CheckboxProps>(
({ className, ...props }, ref) => (
<div className={cn('flex gap-x-2.5', className)}>
<CheckboxPrimitive.Root
ref={ref}
className="peer size-4 shrink-0 rounded-sm border border-icons-1 shadow hover:border-icons-3 disabled:cursor-not-allowed disabled:border-icons-4 data-[state=checked]:border-icons-2 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
className="border-icons-1 hover:border-icons-3 disabled:border-icons-4 data-[state=checked]:border-icons-2 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer size-4 shrink-0 rounded-sm border shadow disabled:cursor-not-allowed"
{...props}
>
<CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
Expand Down
10 changes: 4 additions & 6 deletions packages/ui/src/components/form/control-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ interface ControlGroupProps extends HTMLAttributes<HTMLDivElement> {

/**
* A container component that groups form control elements together.
*
* @param props.type - Specifies the type of control group ('button' or 'input').
* Affects spacing and ARIA labels.
* @param props.children - The form control elements to be grouped (Label, Input/Button,
* ErrorMessage, Caption, etc.).
* @param props.className - Additional CSS classes to apply to the control group.
* @example
* <ControlGroup type="button">
* <Button>Button</Button>
* </ControlGroup>
*/
export function ControlGroup({ children, type, className, ...props }: ControlGroupProps) {
return (
Expand Down
11 changes: 4 additions & 7 deletions packages/ui/src/components/form/fieldset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ interface FieldsetProps extends HTMLAttributes<HTMLFieldSetElement> {

/**
* A form fieldset component that groups related form elements.
* @component
* @param {FieldsetProps} props - The component props
* @param {ReactNode} props.children - The content to be rendered inside the fieldset
* @param {boolean} [props.box] - When true, adds border and padding to create a box around the fieldset
* @param {boolean} [props.shaded] - When true, adds a subtle background color to the fieldset
* @param {string} [props.className] - Additional CSS classes to apply to the fieldset
* @returns {JSX.Element} A styled fieldset element
* @example
* <Fieldset box shaded>
* <div>Form elements</div>
* </Fieldset>
*/
export function Fieldset({ children, box, shaded, className, ...props }: FieldsetProps) {
return (
Expand Down
18 changes: 2 additions & 16 deletions packages/ui/src/components/form/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export interface BaseInputProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>,
VariantProps<typeof inputVariants> {}

const inputVariants = cva('bg-transparent px-2.5 py-1 text-foreground-1 disabled:cursor-not-allowed', {
const inputVariants = cva('text-foreground-1 bg-transparent px-2.5 py-1 disabled:cursor-not-allowed', {
variants: {
variant: {
default:
'flex w-full rounded border text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-foreground-4 focus-visible:rounded focus-visible:outline-none',
'placeholder:text-foreground-4 flex w-full rounded border text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:rounded focus-visible:outline-none',
extended: 'grow border-none focus-visible:outline-none'
},
size: {
Expand Down Expand Up @@ -56,20 +56,6 @@ interface InputProps extends BaseInputProps {

/**
* A form input component with support for labels, captions, and error messages.
*
* @component
* @param {Object} props - The component props
* @param {string} [props.label] - Optional label text displayed above the input
* @param {ReactNode} [props.caption] - Optional caption text displayed below the input
* @param {InputError} [props.error] - Error configuration object containing theme and message
* @param {string} [props.id] - Input element ID, used for label association
* @param {MessageTheme} [props.theme] - Visual theme of the input
* @param {boolean} [props.disabled] - Whether the input is disabled
* @param {boolean} [props.optional] - Indicates if the field is optional, displays "(Optional)" in the label
* @param {string} [props.className] - Optional additional class names for the input element
* @param {string} [props.wrapperClassName] - Optional additional class names for the wrapper element
* @param {BaseInputProps} props.rest - All other props are passed to the underlying input element
*
* @example
* <Input
* label="Email"
Expand Down
13 changes: 3 additions & 10 deletions packages/ui/src/components/form/label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,13 @@ interface LabelProps extends VariantProps<typeof labelVariants>, PropsWithChildr
/**
* A Label component that wraps the Radix UI LabelPrimitive.Root component.
* It supports variant and color styling through class-variance-authority.
*
* @param {Object} props - The properties object.
* @param {string} [props.htmlFor] - The id of the element this label is associated with.
* @param {boolean} [props.optional] - If true, renders "(optional)" next to the label text.
* @param {string} [props.color] - The color variant of the label.
* @param {string} [props.variant] - The style variant of the label.
* @param {React.ReactNode} props.children - The content of the label.
* @param {string} [props.className] - Additional class names to apply to the label.
* @returns {JSX.Element} The rendered label component.
* @example
* <Label htmlFor="label" optional>Label</Label>
*/
const Label = ({ htmlFor, optional, color, variant, children, className }: LabelProps) => {
return (
<LabelRoot htmlFor={htmlFor} variant={variant} color={color} className={className}>
{children} {optional && <span className="align-top text-foreground-7">(optional)</span>}
{children} {optional && <span className="text-foreground-7 align-top">(optional)</span>}
</LabelRoot>
)
}
Expand Down
9 changes: 0 additions & 9 deletions packages/ui/src/components/form/legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,11 @@ interface LegendProps {
/**
* Legend component for form fieldsets
*
* @component
* @example
* ```tsx
* <Legend
* title="Personal Information"
* description="Please fill in your details below"
* />
* ```
*
* @param {object} props - Component props
* @param {ReactNode} props.title - The main title text or element
* @param {ReactNode} [props.description] - Optional description text or element
* @param {string} [props.className] - Optional CSS class name for custom styling
* @returns {JSX.Element} A legend component with title and optional description
*/
export function Legend({ title, description, className }: LegendProps) {
return (
Expand Down
17 changes: 0 additions & 17 deletions packages/ui/src/components/form/message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,16 @@ const themeClassMap: Record<MessageTheme, string> = {

/**
* Message component for displaying status or error messages with different themes.
*
* This component is typically used in forms to provide feedback to users, such as success, warning, or error messages.
*
* @component
* @param {MessageProps} props - The properties for the Message component.
* @param {MessageTheme} props.theme - The visual theme of the message (success, warning, error, or default).
* This also affects accessibility attributes.
* @param {React.ReactNode} props.children - The content of the message to display.
* @param {string} [props.className] - Additional CSS classes to apply to the message container.
*
* @returns {JSX.Element} The rendered Message component.
*
* @example
* ```tsx
* <Message theme={MessageTheme.ERROR}>
* This field is required
* </Message>
*
* <Message theme={MessageTheme.SUCCESS}>
* Changes saved successfully
* </Message>
* ```
*
* @remarks
* - Uses appropriate ARIA roles ('alert' for errors, 'status' for other themes).
* - Sets aria-live appropriately ('assertive' for errors, 'polite' for other themes).
* - Applies theme-specific text colors through Tailwind classes.
*/
export function Message({ children, theme, className }: MessageProps) {
const textClass = themeClassMap[theme]
Expand Down
10 changes: 0 additions & 10 deletions packages/ui/src/components/form/option.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,17 @@ import { cn } from '@utils/cn'

type ControlType = ReactElement<typeof RadioButton> | ReactElement<typeof Checkbox>

/**
* Interface for Option component props
*/
interface OptionProps {
/** The radio button or checkbox control element */
control: ControlType
/** Unique identifier for the input */
id: string
/** Label text for the input */
label?: string
/** Optional description text or node below the label */
description?: string | ReactNode
/** Additional CSS classes */
className?: string
/** Indicates if the item is currently selected */
ariaSelected?: boolean
}

/**
* Individual item that contains a control (radio button or checkbox) with optional label and description
* @component
* @example
* <Option
* control={<RadioButton value="option1" />}
Expand Down
28 changes: 5 additions & 23 deletions packages/ui/src/components/form/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,21 @@ interface SelectError {
message?: string
}

/**
* Props for the Select component
* @interface SelectProps
* @extends {PropsWithChildren}
* @extends {SelectPrimitive.SelectProps}
*/
interface SelectProps extends PropsWithChildren, SelectPrimitive.SelectProps {
/** Label text displayed above the select */
label?: string
/** Error state configuration */
error?: SelectError
/** Optional caption text displayed below the select */
caption?: ReactNode
/** Whether the select is disabled */
disabled?: boolean
/** Placeholder text shown when no option is selected */
placeholder: string
}

/**
* A customizable select component that supports labels, error states, and captions
* @param {SelectProps} props - The component props
* @returns {JSX.Element} The Select component
* @example
* <Select name="select" label="Select an option" placeholder="Select an option">
* <SelectItem value="option1">Option 1</SelectItem>
* <SelectItem value="option2">Option 2</SelectItem>
* </Select>
*/
const Select: FC<SelectProps> = ({
name,
Expand Down Expand Up @@ -72,8 +64,6 @@ const SelectValue = SelectPrimitive.Value

/**
* The trigger button for the select dropdown
* @param {ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & { iconClassName?: string }} props - The component props
* @returns {JSX.Element} The SelectTrigger component
*/
const SelectTrigger = forwardRef<
ElementRef<typeof SelectPrimitive.Trigger>,
Expand All @@ -97,8 +87,6 @@ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName

/**
* The content container for the select dropdown
* @param {ComponentPropsWithoutRef<typeof SelectPrimitive.Content>} props - The component props
* @returns {JSX.Element} The SelectContent component
*/
const SelectContent = forwardRef<
ElementRef<typeof SelectPrimitive.Content>,
Expand Down Expand Up @@ -132,8 +120,6 @@ SelectContent.displayName = SelectPrimitive.Content.displayName

/**
* A label component for grouping select items
* @param {ComponentPropsWithoutRef<typeof SelectPrimitive.Label>} props - The component props
* @returns {JSX.Element} The SelectLabel component
*/
const SelectLabel = forwardRef<
ElementRef<typeof SelectPrimitive.Label>,
Expand All @@ -145,8 +131,6 @@ SelectLabel.displayName = SelectPrimitive.Label.displayName

/**
* An individual selectable item within the dropdown
* @param {ComponentPropsWithoutRef<typeof SelectPrimitive.Item>} props - The component props
* @returns {JSX.Element} The SelectItem component
*/
const SelectItem = forwardRef<
ElementRef<typeof SelectPrimitive.Item>,
Expand All @@ -172,8 +156,6 @@ SelectItem.displayName = SelectPrimitive.Item.displayName

/**
* A visual separator between select items
* @param {ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>} props - The component props
* @returns {JSX.Element} The SelectSeparator component
*/
const SelectSeparator = forwardRef<
ElementRef<typeof SelectPrimitive.Separator>,
Expand Down
3 changes: 0 additions & 3 deletions packages/ui/src/components/form/separator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ interface FormSeparatorProps {

/**
* A horizontal separator component for forms
* @component
* @param {FormSeparatorProps} props - The component props
* @returns {JSX.Element} A horizontal separator element
* @example
* // Basic usage
* <FormSeparator />
Expand Down
18 changes: 2 additions & 16 deletions packages/ui/src/components/form/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,22 @@ import { forwardRef, ReactNode, TextareaHTMLAttributes } from 'react'
import { Caption, ControlGroup, Label, Message, MessageTheme } from '@/components'
import { cn } from '@utils/cn'

/**
* Interface for the error object used in the Textarea component.
*/
interface TextareaError {
/** Theme for the error message. */
theme: MessageTheme
/** Optional error message to display. */
message?: string
}

/**
* Props for the Textarea component.
*/
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
/** Label for the textarea. */
label?: string
/** Caption text displayed below the textarea. */
caption?: ReactNode
/** Error object containing theme and optional message. */
error?: TextareaError
/** Indicates if the textarea is optional. */
optional?: boolean
}

/**
* A forward-ref Textarea component with support for labels, captions, and error messages.
*
* @param {TextareaProps} props - The props for the Textarea component.
* @param {React.Ref<HTMLTextAreaElement>} ref - The ref to be forwarded to the textarea element.
* @returns {JSX.Element} The rendered Textarea component.
* @example
* <Textarea name="textarea" label="Textarea" placeholder="Enter text here" />
*/
const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
({ id, disabled, label, caption, error, optional, className, ...props }, ref) => {
Expand Down

0 comments on commit ed675cd

Please sign in to comment.