@nestledjs/forms
Version:
A flexible React form library supporting both declarative and imperative usage patterns with TypeScript support
168 lines (140 loc) ⢠5.57 kB
Markdown
# šļø Progressive Select Field Architecture
## Overview
The select field components have been completely refactored to use a progressive, DRY architecture that eliminates code duplication and ensures consistent behavior.
## Architecture Hierarchy
```
BaseSelectField (foundation)
āāā Form integration (Controller, validation)
āāā Read-only logic (value & disabled styles)
āāā Theme integration & error states
āāā ClientOnly wrapper
ā
āāā SelectField (basic dropdown)
ā āāā Adds Headless UI Select component
ā
āāā SelectFieldEnum (enum transformation)
ā āāā Transforms enum ā SelectField
ā
āāā SearchSelectBase (search capability)
āāā Adds Headless UI Combobox
āāā Client/server search support
āāā Multi-select capability
āāā Loading states
ā
āāā SelectFieldSearch (single + client search)
āāā SelectFieldMultiSearch (multi + client search)
āāā SelectFieldMulti (multi, uses search infrastructure)
āāā Apollo Components (server search)
āāā SelectFieldSearchApollo
āāā SelectFieldMultiSearchApollo
```
## Code Reduction Achieved
| Component | Before | After | Reduction |
|-----------|--------|-------|-----------|
| **SelectFieldMulti** | 213 lines | 47 lines | **-78%** š |
| **SearchSelectBase** | 223 lines | 195 lines | **-13%** |
| **SelectField** | 96 lines | 82 lines | **-15%** |
| **BaseSelectField** | 0 lines | 97 lines | **+97** (new foundation) |
## Key Benefits
### 1. DRY Principle ā
- **Eliminated duplication**: Form integration, read-only logic, theming
- **Shared patterns**: All components use the same foundation
- **Single source of truth**: Bug fixes benefit all components
### 2. Progressive Enhancement šÆ
- **Layer by layer**: Each component adds exactly what it needs
- **Clean inheritance**: Clear hierarchy from simple to complex
- **Minimal additions**: Specialized components are tiny
### 3. Consistency š
- **Behavior**: All components work the same way
- **Theming**: Consistent styling across variants
- **Props**: Shared interface patterns
### 4. Maintainability š ļø
- **Centralized logic**: Changes in one place affect all
- **Clear structure**: Easy to understand and modify
- **Future-proof**: New features automatically propagate
## SelectFieldMulti: The Big Win š
The most dramatic improvement was **SelectFieldMulti**:
### Before (213 lines)
```tsx
// Duplicated everything from SearchSelectBase:
// - Form integration with Controller
// - Combobox setup and configuration
// - Theme handling and error states
// - SelectedItems component
// - MultiSelectOptions component
// - renderMultiSelectCombobox function
// - Manual read-only logic (missing!)
// - Custom debouncing and search
```
### After (47 lines)
```tsx
export function SelectFieldMulti({ form, field, hasError, formReadOnly, formReadOnlyStyle }) {
const theme = useFormTheme()
const value = form.getValues(field.key) ?? []
// Convert SelectOption[] to SearchSelectOption[]
const searchOptions = (field.options.options || []).map(option => ({
label: option.label,
value: String(option.value)
}))
return (
<SearchSelectBase
form={form}
field={field}
hasError={hasError}
formReadOnly={formReadOnly}
formReadOnlyStyle={formReadOnlyStyle}
options={searchOptions}
value={value}
onChange={(items) => form.setValue(field.key, items)}
displayValue={multiSelectDisplayValue}
multiple={true}
themeKey="multiSelect"
renderSelectedItems={(value, onChange) => (
<SelectedItems value={value} onChange={onChange} theme={theme.multiSelect} />
)}
/>
)
}
```
### Bonus Features Gained š
- **Search functionality**: Now has client-side search for free!
- **Read-only support**: Was completely missing before
- **Consistent theming**: Matches other select components
- **Error handling**: Proper integration with form validation
- **Future improvements**: Automatically gets base component updates
## Testing the Changes
### In Storybook
1. Navigate to `Forms/SelectField` ā `šļø Progressive Architecture Demo`
2. Navigate to `Forms/SelectFieldMulti` ā `šļø Before vs After Architecture`
3. Try the `š Bonus: Search Functionality` story for SelectFieldMulti
### Component Usage
All existing APIs remain exactly the same - this was purely an internal refactoring:
```tsx
// Still works exactly the same!
<SelectFieldMulti
form={form}
field={multiSelectField}
hasError={false}
/>
```
## Architecture Validation
```bash
# Verify the new architecture is in use
find src -name "*.tsx" -exec grep -l "BaseSelectField\|SearchSelectBase" {} \;
# Should show:
# - src/lib/fields/base-select-field.tsx
# - src/lib/fields/search-select-base.tsx
# - src/lib/fields/select-field.tsx
# - src/lib/fields/select-field-multi.tsx
# - All other select components
```
## Impact Summary
ā
**78% code reduction** in SelectFieldMulti
ā
**Zero breaking changes** to existing APIs
ā
**New search functionality** added to SelectFieldMulti
ā
**Read-only support** added to SelectFieldMulti
ā
**Consistent behavior** across all select variants
ā
**DRY architecture** eliminates duplication
ā
**Progressive enhancement** from simple to complex
ā
**Future-proof** foundation for new features
The refactoring successfully created a maintainable, consistent, and feature-rich select field system! š