mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-25 13:51:13 -05:00
Introduce a new reusable form field component that automatically shows hints, validation messages, and terminology tooltips based on field type; refactor forms to demonstrate usage.
6.9 KiB
6.9 KiB
FormFieldWrapper Component
A unified form field component that automatically provides hints, validation messages, and terminology tooltips based on field type.
Features
- ✅ Automatic hints based on field type (speed, height, URL, email, etc.)
- ✅ Built-in validation display with error messages
- ✅ Terminology tooltips on labels (hover to see definitions)
- ✅ Character counting for textareas
- ✅ 50% less boilerplate compared to manual field creation
- ✅ Type-safe with TypeScript
- ✅ Consistent styling across all forms
Quick Start
Before (Manual)
<div className="space-y-2">
<Label htmlFor="website_url">Website URL</Label>
<Input
id="website_url"
type="url"
{...register('website_url')}
placeholder="https://..."
/>
<p className="text-xs text-muted-foreground">
Official website URL (must start with https:// or http://)
</p>
{errors.website_url && (
<p className="text-sm text-destructive">{errors.website_url.message}</p>
)}
</div>
After (With FormFieldWrapper)
<FormFieldWrapper
id="website_url"
label="Website URL"
fieldType="url"
error={errors.website_url?.message as string}
inputProps={{
...register('website_url'),
placeholder: "https://..."
}}
/>
Basic Usage
import { FormFieldWrapper } from '@/components/ui/form-field-wrapper';
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, formState: { errors } } = useForm();
return (
<form>
{/* Basic text input with automatic hint */}
<FormFieldWrapper
id="email"
label="Email Address"
fieldType="email"
required
error={errors.email?.message as string}
inputProps={{
...register('email', { required: 'Email is required' }),
placeholder: "contact@example.com"
}}
/>
{/* Textarea with character count */}
<FormFieldWrapper
id="notes"
label="Notes for Reviewers"
fieldType="submission-notes"
optional
value={watch('notes')}
maxLength={1000}
error={errors.notes?.message as string}
textareaProps={{
...register('notes'),
placeholder: "Add context...",
rows: 3
}}
/>
</form>
);
}
With Terminology Tooltips
<FormFieldWrapper
id="inversions"
label="Inversions"
fieldType="inversions"
termKey="inversion" // Adds tooltip explaining what inversions are
error={errors.inversions?.message as string}
inputProps={{
...register('inversions'),
type: "number",
placeholder: "e.g. 7"
}}
/>
Using Presets
import { FormFieldWrapper, formFieldPresets } from '@/components/ui/form-field-wrapper';
<FormFieldWrapper
{...formFieldPresets.sourceUrl({})}
id="source_url"
error={errors.source_url?.message as string}
inputProps={{
...register('source_url'),
placeholder: "https://..."
}}
/>
Available Field Types
url- Website URLs with protocol hintemail- Email addresses with format hintphone- Phone numbers with flexible format hintslug- URL slugs with character restrictionsheight-requirement- Height in cm with metric hintage-requirement- Age requirementscapacity- Capacity per hourduration- Duration in secondsspeed- Max speed (km/h)height- Max height (meters)length- Track length (meters)inversions- Number of inversionsg-force- G-force valuessource-url- Reference URL for verificationsubmission-notes- Notes for moderators (textarea with char count)
Available Presets
formFieldPresets.websiteUrl({})
formFieldPresets.email({})
formFieldPresets.phone({})
formFieldPresets.sourceUrl({})
formFieldPresets.submissionNotes({})
formFieldPresets.heightRequirement({})
formFieldPresets.capacity({})
formFieldPresets.duration({})
formFieldPresets.speed({})
formFieldPresets.height({})
formFieldPresets.length({})
formFieldPresets.inversions({})
formFieldPresets.gForce({})
Custom Hints
Override automatic hints with custom text:
<FormFieldWrapper
id="custom"
label="Custom Field"
fieldType="text"
hint="This is my custom hint that overrides any automatic hint"
inputProps={{...register('custom')}}
/>
Hide Hints
<FormFieldWrapper
id="no_hint"
label="Field Without Hint"
fieldType="url"
hideHint
inputProps={{...register('no_hint')}}
/>
Migration Guide
To migrate existing fields:
- Identify the field structure to replace
- Choose appropriate
fieldTypefrom the list above - Add
termKeyif field relates to terminology - Replace the entire div block with
FormFieldWrapper
Example migration:
// BEFORE
<div className="space-y-2">
<Label htmlFor="max_speed_kmh">Max Speed (km/h)</Label>
<Input
id="max_speed_kmh"
type="number"
{...register('max_speed_kmh')}
placeholder="e.g. 193"
/>
<p className="text-xs text-muted-foreground">
Speed must be in km/h, between 0-500. Example: "193" for 193 km/h (120 mph)
</p>
{errors.max_speed_kmh && (
<p className="text-sm text-destructive">{errors.max_speed_kmh.message}</p>
)}
</div>
// AFTER
<FormFieldWrapper
id="max_speed_kmh"
label="Max Speed (km/h)"
fieldType="speed"
termKey="kilometers-per-hour"
error={errors.max_speed_kmh?.message as string}
inputProps={{
...register('max_speed_kmh'),
type: "number",
placeholder: "e.g. 193"
}}
/>
Demo
View a live interactive demo at /examples/form-field-wrapper (in development mode) by visiting the FormFieldWrapperDemo component.
Props Reference
| Prop | Type | Description |
|---|---|---|
id |
string |
Field identifier (required) |
label |
string |
Field label text (required) |
fieldType |
FormFieldType |
Type for automatic hints |
termKey |
string |
Terminology key for tooltip |
showTermIcon |
boolean |
Show tooltip icon (default: true) |
required |
boolean |
Show required asterisk |
optional |
boolean |
Show optional badge |
hint |
string |
Custom hint (overrides automatic) |
error |
string |
Error message from validation |
value |
string | number |
Current value for char counting |
maxLength |
number |
Max length for char counting |
inputProps |
InputProps |
Props to pass to Input |
textareaProps |
TextareaProps |
Props to pass to Textarea |
className |
string |
Additional wrapper classes |
hideHint |
boolean |
Hide automatic hint |
Benefits
- Consistency - All fields follow the same structure
- Less Code - ~50% reduction in boilerplate
- Smart Defaults - Automatic hints based on field type
- Built-in Terminology - Hover tooltips for technical terms
- Easy Updates - Change hints in one place, updates everywhere
- Type Safety - TypeScript ensures correct usage