# FormFieldWrapper Component

The `FormFieldWrapper` component provides consistent labeling, hints, error states, and accessibility features around form controls. It wraps any form input element and provides standardized styling and behavior.

## Features

- **Accessibility**: Full ARIA support with proper label association, error announcements, and required field indicators
- **State Management**: Supports default, error, success, and disabled states
- **Flexible**: Works with any form control (input, select, textarea, etc.)
- **Tokenized**: Uses design tokens for consistent theming
- **Responsive**: Adapts to different screen sizes

## Usage

### Basic Example

```html
<ccl-form-field-wrapper label="Email Address">
  <input type="email" id="email" />
</ccl-form-field-wrapper>
```

### With Required Field

```html
<ccl-form-field-wrapper label="Password" required>
  <input type="password" id="password" />
</ccl-form-field-wrapper>
```

### With Hint Text

```html
<ccl-form-field-wrapper 
  label="Email" 
  hint="We'll never share your email">
  <input type="email" id="email" />
</ccl-form-field-wrapper>
```

### With Error State

```html
<ccl-form-field-wrapper 
  label="Email" 
  error="Invalid email address">
  <input type="email" id="email" />
</ccl-form-field-wrapper>
```

### With Success State

```html
<ccl-form-field-wrapper 
  label="Email" 
  state="success">
  <input type="email" id="email" />
</ccl-form-field-wrapper>
```

### With Disabled State

```html
<ccl-form-field-wrapper 
  label="Email" 
  state="disabled">
  <input type="email" id="email" disabled />
</ccl-form-field-wrapper>
```

### Complete Example

```html
<ccl-form-field-wrapper 
  label="Email Address" 
  required 
  hint="We'll never share your email"
  error="Invalid email address"
  for="user-email">
  <input type="email" id="user-email" />
</ccl-form-field-wrapper>
```

## API Reference

### Inputs

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `label` | `string` | `''` | The visible field label |
| `required` | `boolean` | `false` | Adds visual asterisk (*) and ARIA required attribute |
| `hint` | `string` | `''` | Helper text displayed below the field |
| `error` | `string` | `''` | Error message displayed in red |
| `state` | `'default' \| 'error' \| 'success' \| 'disabled'` | `'default'` | Visual state of the field |
| `for` | `string` | `''` | ID of the wrapped input (for accessibility linkage) |

### Content Projection

The component uses `<ng-content>` to project any form control element. The wrapped element should have an `id` attribute that matches the `for` input or will be automatically generated.

## Accessibility Features

### Label Association
- Labels are properly associated with inputs using the `for` attribute
- Screen readers announce the label when focusing the input

### Error Announcements
- Error messages are announced via `aria-describedby`
- Error messages have `role="alert"` for immediate screen reader attention

### Required Field Indicators
- Visual asterisk (*) for required fields
- `aria-required="true"` attribute on the input
- `aria-label="required"` on the asterisk

### State Management
- `aria-invalid="true"` when in error state
- Proper color contrast for all states
- Focus management and keyboard navigation

## Styling

The component uses CSS custom properties (design tokens) for consistent theming:

### CSS Classes

- `.ccl-form-field-wrapper` - Main wrapper
- `.ccl-form-field-wrapper__label` - Field label
- `.ccl-form-field-wrapper__required` - Required asterisk
- `.ccl-form-field-wrapper__input-container` - Input container
- `.ccl-form-field-wrapper__hint` - Helper text
- `.ccl-form-field-wrapper__error` - Error message

### State Modifiers

- `.ccl-form-field-wrapper--disabled` - Disabled state
- `.ccl-form-field-wrapper--error` - Error state
- `.ccl-form-field-wrapper--success` - Success state

### Design Tokens Used

- `--color-text` - Label text color
- `--color-error-500` - Error text and required asterisk color
- `--color-success-500` - Success state color
- `--color-muted` - Hint text and disabled state color
- `--color-primary-500` - Focus state color
- `--typography-font-family-sans` - Font family
- `--typography-font-size-sm` - Small text size
- `--typography-font-size-md` - Medium text size
- `--typography-font-weight-regular` - Regular font weight
- `--typography-font-weight-bold` - Bold font weight
- `--spacing-xs` - Extra small spacing
- `--spacing-sm` - Small spacing

## Behavior

### Error Priority
When both `error` text and `state="success"` are provided, the error state takes priority and the success styling is not applied.

### Hint vs Error
When both `hint` and `error` are provided, only the error message is displayed (hint is hidden).

### ID Management
- If `for` is provided, it's used as the field ID
- If `for` is not provided, a unique ID is generated
- The input element's `id` is automatically set if not already present

## Examples with Different Form Controls

### Text Input
```html
<ccl-form-field-wrapper label="Name" required>
  <input type="text" id="name" />
</ccl-form-field-wrapper>
```

### Select Dropdown
```html
<ccl-form-field-wrapper label="Country" hint="Select your country">
  <ccl-select 
    id="country" 
    placeholder="Choose..."
    [options]="countryOptions">
  </ccl-select>
</ccl-form-field-wrapper>
```

### Textarea
```html
<ccl-form-field-wrapper 
  label="Message" 
  hint="Maximum 500 characters"
  required>
  <ccl-textarea 
    id="message" 
    rows="4" 
    maxLength="500"
    placeholder="Enter your message">
  </ccl-textarea>
</ccl-form-field-wrapper>
```

### Custom Input Component
```html
<ccl-form-field-wrapper 
  label="Search" 
  hint="Enter search terms">
  <ccl-input id="search" placeholder="Search..."></ccl-input>
</ccl-form-field-wrapper>
```

## Complete Form Example

Here's a comprehensive example showing all form components working together with FormFieldWrapper:

### HTML Template
```html
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()">
  <div class="form-grid">
    <!-- Personal Information -->
    <ccl-form-field-wrapper 
      label="First Name" 
      [required]="true"
      [error]="getFieldError('firstName', 'First name is required')">
      <ccl-input 
        id="firstName"
        formControlName="firstName"
        placeholder="Enter your first name">
      </ccl-input>
    </ccl-form-field-wrapper>

    <ccl-form-field-wrapper 
      label="Last Name" 
      [required]="true"
      [error]="getFieldError('lastName', 'Last name is required')">
      <ccl-input 
        id="lastName"
        formControlName="lastName"
        placeholder="Enter your last name">
      </ccl-input>
    </ccl-form-field-wrapper>

    <!-- Contact Information -->
    <ccl-form-field-wrapper 
      label="Email Address" 
      [required]="true"
      hint="We'll never share your email"
      [error]="getFieldError('email', 'Please enter a valid email address')">
      <ccl-input 
        id="email"
        type="email"
        formControlName="email"
        placeholder="your.email@example.com">
      </ccl-input>
    </ccl-form-field-wrapper>

    <ccl-form-field-wrapper 
      label="Phone Number" 
      hint="Include country code"
      [error]="getFieldError('phone', 'Please enter a valid phone number')">
      <ccl-input 
        id="phone"
        type="text"
        formControlName="phone"
        placeholder="+1 (555) 123-4567">
      </ccl-input>
    </ccl-form-field-wrapper>

    <!-- Location -->
    <ccl-form-field-wrapper 
      label="Country" 
      [required]="true"
      [error]="getFieldError('country', 'Please select your country')">
      <ccl-select 
        id="country"
        formControlName="country"
        placeholder="Choose your country"
        [options]="countryOptions"
        (selectionChange)="onCountryChange($event)">
      </ccl-select>
    </ccl-form-field-wrapper>

    <ccl-form-field-wrapper 
      label="State/Province" 
      [error]="getFieldError('state', 'Please select your state or province')">
      <ccl-select 
        id="state"
        formControlName="state"
        placeholder="Choose your state"
        [options]="stateOptions"
        [disabled]="!contactForm.get('country')?.value">
      </ccl-select>
    </ccl-form-field-wrapper>

    <!-- Preferences -->
    <ccl-form-field-wrapper 
      label="Interests" 
      hint="Select all that apply"
      [error]="getFieldError('interests', 'Please select at least one interest')">
      <ccl-select 
        id="interests"
        formControlName="interests"
        [multiple]="true"
        [size]="4"
        [options]="interestOptions">
      </ccl-select>
    </ccl-form-field-wrapper>

    <ccl-form-field-wrapper 
      label="Preferred Contact Method" 
      [required]="true"
      [error]="getFieldError('contactMethod', 'Please select a contact method')">
      <ccl-select 
        id="contactMethod"
        formControlName="contactMethod"
        placeholder="How should we contact you?"
        [options]="contactMethodOptions">
      </ccl-select>
    </ccl-form-field-wrapper>

    <!-- Message -->
    <ccl-form-field-wrapper 
      label="Message" 
      [required]="true"
      hint="Tell us about your inquiry (minimum 50 characters)"
      [error]="getFieldError('message', 'Message must be at least 50 characters')">
      <ccl-textarea 
        id="message"
        formControlName="message"
        [rows]="5"
        [minLength]="50"
        [maxLength]="1000"
        resize="vertical"
        placeholder="Describe your inquiry in detail...">
      </ccl-textarea>
    </ccl-form-field-wrapper>

    <!-- Additional Information -->
    <ccl-form-field-wrapper 
      label="Additional Notes" 
      hint="Optional - any additional information"
      [error]="getFieldError('notes', 'Notes cannot exceed 500 characters')">
      <ccl-textarea 
        id="notes"
        formControlName="notes"
        [rows]="3"
        [maxLength]="500"
        resize="none"
        placeholder="Any additional information...">
      </ccl-textarea>
    </ccl-form-field-wrapper>

    <!-- Date Field -->
    <ccl-form-field-wrapper 
      label="Preferred Contact Date" 
      hint="When would you like us to contact you?"
      [error]="getFieldError('preferredDate', 'Please select a valid date')">
      <ccl-datefield 
        id="preferredDate"
        formControlName="preferredDate"
        placeholder="Select a date">
      </ccl-datefield>
    </ccl-form-field-wrapper>
  </div>

  <!-- Form Actions -->
  <div class="form-actions">
    <ccl-button 
      type="button" 
      variant="tertiary"
      (click)="onReset()">
      Reset Form
    </ccl-button>
    
    <ccl-button 
      type="submit" 
      variant="primary"
      [disabled]="contactForm.invalid || isSubmitting"
      [loading]="isSubmitting">
      {{ isSubmitting ? 'Sending...' : 'Send Message' }}
    </ccl-button>
  </div>
</form>
```

### TypeScript Component
```typescript
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { 
  FormFieldWrapperComponent, 
  InputComponent, 
  SelectComponent, 
  TextareaComponent,
  DateFieldComponent,
  ButtonComponent,
  SelectOption 
} from '@Crystal-Code-Labs/ccl-ui-components';

@Component({
  selector: 'app-contact-form',
  templateUrl: './contact-form.component.html',
  imports: [
    ReactiveFormsModule,
    FormFieldWrapperComponent,
    InputComponent,
    SelectComponent,
    TextareaComponent,
    DateFieldComponent,
    ButtonComponent
  ]
})
export class ContactFormComponent implements OnInit {
  contactForm: FormGroup;
  isSubmitting = false;

  // Select options
  countryOptions: SelectOption[] = [
    { value: 'us', label: 'United States' },
    { value: 'ca', label: 'Canada' },
    { value: 'mx', label: 'Mexico' },
    { value: 'uk', label: 'United Kingdom' },
    { value: 'de', label: 'Germany' },
    { value: 'fr', label: 'France' }
  ];

  stateOptions: SelectOption[] = [];

  interestOptions: SelectOption[] = [
    { value: 'web-dev', label: 'Web Development' },
    { value: 'mobile-dev', label: 'Mobile Development' },
    { value: 'ui-ux', label: 'UI/UX Design' },
    { value: 'data-science', label: 'Data Science' },
    { value: 'ai-ml', label: 'AI/ML' },
    { value: 'devops', label: 'DevOps' },
    { value: 'cloud', label: 'Cloud Computing' }
  ];

  contactMethodOptions: SelectOption[] = [
    { value: 'email', label: 'Email' },
    { value: 'phone', label: 'Phone Call' },
    { value: 'video', label: 'Video Call' },
    { value: 'sms', label: 'SMS' }
  ];

  constructor(private fb: FormBuilder) {
    this.contactForm = this.createForm();
  }

  ngOnInit() {
    // Set up state options based on country
    this.contactForm.get('country')?.valueChanges.subscribe(country => {
      this.updateStateOptions(country);
      this.contactForm.get('state')?.setValue('');
    });
  }

  private createForm(): FormGroup {
    return this.fb.group({
      firstName: ['', [Validators.required, Validators.minLength(2)]],
      lastName: ['', [Validators.required, Validators.minLength(2)]],
      email: ['', [Validators.required, Validators.email]],
      phone: ['', [Validators.pattern(/^\+?[\d\s\-\(\)]+$/)]],
      country: ['', Validators.required],
      state: [''],
      interests: [[], [Validators.required, Validators.minLength(1)]],
      contactMethod: ['', Validators.required],
      message: ['', [Validators.required, Validators.minLength(50), Validators.maxLength(1000)]],
      notes: ['', Validators.maxLength(500)],
      preferredDate: ['']
    });
  }

  private updateStateOptions(country: string) {
    const stateMap: { [key: string]: SelectOption[] } = {
      'us': [
        { value: 'ca', label: 'California' },
        { value: 'ny', label: 'New York' },
        { value: 'tx', label: 'Texas' },
        { value: 'fl', label: 'Florida' }
      ],
      'ca': [
        { value: 'on', label: 'Ontario' },
        { value: 'bc', label: 'British Columbia' },
        { value: 'qc', label: 'Quebec' },
        { value: 'ab', label: 'Alberta' }
      ],
      'mx': [
        { value: 'cdmx', label: 'Mexico City' },
        { value: 'jal', label: 'Jalisco' },
        { value: 'nue', label: 'Nuevo León' }
      ]
    };

    this.stateOptions = stateMap[country] || [];
  }

  onCountryChange(country: string | string[]) {
    const countryValue = Array.isArray(country) ? country[0] : country;
    this.updateStateOptions(countryValue);
    this.contactForm.get('state')?.setValue('');
  }

  onStateChange(state: string | string[]) {
    const stateValue = Array.isArray(state) ? state[0] : state;
    // Handle state change if needed
  }

  onInterestsChange(interests: string | string[]) {
    const interestsValue = Array.isArray(interests) ? interests : [interests];
    // Handle interests change if needed
  }

  onContactMethodChange(method: string | string[]) {
    const methodValue = Array.isArray(method) ? method[0] : method;
    // Handle contact method change if needed
  }

  getFieldError(fieldName: string, defaultMessage: string): string {
    const field = this.contactForm.get(fieldName);
    if (field?.invalid && field?.touched) {
      return defaultMessage;
    }
    return '';
  }

  onSubmit() {
    if (this.contactForm.valid) {
      this.isSubmitting = true;
      
      // Simulate API call
      setTimeout(() => {
        console.log('Form submitted:', this.contactForm.value);
        this.isSubmitting = false;
        this.onReset();
      }, 2000);
    } else {
      // Mark all fields as touched to show validation errors
      Object.keys(this.contactForm.controls).forEach(key => {
        this.contactForm.get(key)?.markAsTouched();
      });
    }
  }

  onReset() {
    // Reset all form controls to their initial values
    this.contactForm.reset();
    
    // Clear dynamic options
    this.stateOptions = [];
    
    // Reset form state
    this.contactForm.markAsUntouched();
    this.contactForm.markAsPristine();
    
    // Reset individual form controls to ensure clean state
    Object.keys(this.contactForm.controls).forEach(key => {
      const control = this.contactForm.get(key);
      control?.markAsUntouched();
      control?.markAsPristine();
      control?.setErrors(null);
    });
    
    // IMPORTANT: Explicitly set form control values to trigger writeValue on custom components
    // This ensures that ControlValueAccessor components are properly reset
    this.contactForm.patchValue({
      firstName: '',
      lastName: '',
      email: '',
      phone: '',
      country: '',
      state: '',
      interests: [],
      contactMethod: '',
      preferredDate: null,
      message: '',
      notes: ''
    });
    
    // Force change detection to ensure error messages are cleared
    setTimeout(() => {
      this.contactForm.updateValueAndValidity();
    }, 0);
  }
}
```

### CSS Styling
```css
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--spacing-md);
  margin-bottom: var(--spacing-lg);
}

.form-actions {
  display: flex;
  gap: var(--spacing-sm);
  justify-content: flex-end;
  margin-top: var(--spacing-lg);
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--color-border);
}

@media (max-width: 768px) {
  .form-grid {
    grid-template-columns: 1fr;
    gap: var(--spacing-sm);
  }
  
  .form-actions {
    flex-direction: column;
  }
}
```

This complete form example demonstrates:

- **All Form Components**: Input, Select, Textarea, Date Field, and Button
- **FormFieldWrapper Integration**: Consistent labeling, hints, and error handling
- **Reactive Forms**: Angular reactive forms with validation
- **Dynamic Options**: State options that change based on country selection
- **Multiple Selection**: Interests selection with multiple options
- **Validation**: Real-time validation with error messages
- **Accessibility**: Proper ARIA attributes and screen reader support
- **Responsive Design**: Mobile-friendly grid layout
- **Loading States**: Submit button with loading indicator
- **Form Reset**: Reset functionality

## Testing

The component includes comprehensive unit tests covering:
- Input property handling
- ID generation and management
- State management logic
- Template rendering
- CSS class application
- Accessibility attributes
- Content projection
- Error priority handling

Run tests with:
```bash
ng test
```

## Migration from Existing Components

If you're currently using individual input components with built-in labels and error handling, you can migrate to use `FormFieldWrapper` for more consistent behavior:

### Before
```html
<ccl-input 
  label="Email" 
  error="Invalid email"
  helperText="We'll never share your email">
</ccl-input>
```

### After
```html
<ccl-form-field-wrapper 
  label="Email" 
  error="Invalid email"
  hint="We'll never share your email">
  <ccl-input id="email"></ccl-input>
</ccl-form-field-wrapper>
```

This approach provides better separation of concerns and more flexible composition.
