Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 372x 372x 372x 372x 372x 372x 372x 124x 124x 124x 124x 124x 1x 1x 1x 1x 1x | 'use client';
/**
* FormSelect Component
*
* A select dropdown component designed to work with the form validation framework.
* Wraps the base Input component with touched state support and
* optimized error display.
*
* Supports both legacy props (error, disabled) and standard props
* (isError, isDisabled, errorMessage) for backward compatibility.
*/
import { forwardRef } from 'react';
import { Input } from '../Input';
import { cn } from '@/lib/core';
import type { CommonSize, StateProps } from '../types';
export interface SelectOption {
/** Option value */
value: string;
/** Display label */
label: string;
/** Whether option is disabled */
disabled?: boolean;
}
export interface FormSelectProps
extends StateProps,
Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
/** Select label */
label: string;
/** Select options */
options: SelectOption[];
/**
* Validation error message (standard prop)
* Takes precedence over legacy `error` prop
*/
errorMessage?: string;
/**
* @deprecated Use `errorMessage` instead
* Validation error message (legacy prop)
*/
error?: string | null;
/** Whether the field has been touched/blurred */
touched?: boolean;
/** Helper text shown below the select */
helperText?: string;
/** Placeholder text for empty selection */
placeholder?: string;
/** Select size variant */
size?: CommonSize;
/** Additional wrapper class name */
wrapperClassName?: string;
}
/**
* FormSelect Component
*
* A select dropdown component that integrates with the form validation framework.
* Only shows errors when the field has been touched.
*
* @example
* ```tsx
* // With standard props
* <FormSelect
* label="Country"
* name="country"
* options={[
* { value: 'us', label: 'United States' },
* { value: 'ca', label: 'Canada' }
* ]}
* value={country}
* onChange={handleChange}
* isError={!!errors.country}
* errorMessage={errors.country}
* required
* />
*
* // With form validation framework
* const { errors, touched, getFieldProps } = useForm({ ... });
*
* <FormSelect
* label="Country"
* options={countryOptions}
* {...getFieldProps('country')}
* error={errors.country}
* touched={touched.country}
* required
* />
* ```
*/
export const FormSelect = forwardRef<HTMLSelectElement, FormSelectProps>(
(
{
label,
options,
// Standard props
errorMessage,
isLoading,
isDisabled,
isError,
// Legacy props
error,
touched,
// Common props
helperText,
placeholder,
size = 'md',
wrapperClassName,
className,
id,
required,
disabled,
value,
...props
},
ref
) => {
// Resolve error message (prefer standard prop, convert null to undefined)
const resolvedErrorMessage = errorMessage ?? error ?? undefined;
// Only show error if the field has been touched (when using touched prop)
// or if isError is explicitly set
const hasError = isError ?? (touched ? !!resolvedErrorMessage : false);
const displayedError = hasError ? resolvedErrorMessage : undefined;
// Resolve disabled state (combine standard and legacy)
const resolvedDisabled = isDisabled ?? disabled;
return (
<div className={cn('form-select-wrapper', wrapperClassName)}>
<Input
ref={ref as React.Ref<HTMLSelectElement>}
as="select"
id={id}
label={label}
error={displayedError}
helperText={helperText}
size={size}
required={required}
disabled={resolvedDisabled || isLoading}
fullWidth
className={className}
value={value}
{...props}
>
{placeholder && (
<option value="" disabled>
{placeholder}
</option>
)}
{options.map((option) => (
<option
key={option.value}
value={option.value}
disabled={option.disabled}
>
{option.label}
</option>
))}
</Input>
</div>
);
}
);
FormSelect.displayName = 'FormSelect';
export default FormSelect;
|