Files
leptos-shadcn-ui/docs/remediation/components/input/REMEDIATION_PLAN.md
Peter Hanssens ec459d142c 🔧 Complete refactoring implementation for v0.9.0
- All 9 components fully refactored with modular architecture
- 45+ test modules created and organized
- File size compliance achieved (99% reduction)
- Enterprise-grade code quality implemented
- All compilation issues resolved

Ready for v0.9.0 release publication!
2025-09-22 13:06:22 +10:00

14 KiB

🔧 Input Component Remediation Plan

Priority 1: Critical Component - Immediate Action Required

🚨 Critical Issues Summary

The Input component has severe issues that make it unsuitable for production use:

  • 8 test files with stub implementations
  • Tests marked as "will fail initially"
  • ~30% test coverage (estimated)
  • Missing validation system
  • Incomplete accessibility features
  • File size violations (224+ lines)

🎯 Remediation Strategy

Phase 1: Emergency Fixes (Week 1)

Day 1-2: Remove Stub Tests

Current Problem:

#[test]
fn test_input_basic_rendering() {
    // This test will fail initially - we need to implement proper rendering
}

Target Implementation:

#[test]
fn test_input_basic_rendering() {
    let input = Input::new(InputProps::default());
    let rendered = input.render();
    assert!(rendered.contains("input"));
    assert!(rendered.contains("class"));
}

Files to Fix:

  • packages/leptos/input/src/tdd_tests/basic_rendering_tests.rs (224 lines)
  • packages/leptos/input/src/tdd_tests/validation_tests.rs (180+ lines)
  • packages/leptos/input/src/tdd_tests/accessibility_tests.rs (150+ lines)
  • packages/leptos/input/src/tdd_tests/performance_tests.rs (140+ lines)

Day 3-4: Implement Validation System

Current Problem: No validation system implemented

Target Implementation:

// Create validation module
pub mod validation {
    use super::*;
    
    #[derive(Debug, Clone, PartialEq)]
    pub enum ValidationRule {
        Required,
        MinLength(usize),
        MaxLength(usize),
        Email,
        Pattern(String),
    }
    
    #[derive(Debug, Clone, PartialEq)]
    pub struct ValidationResult {
        pub is_valid: bool,
        pub errors: Vec<String>,
    }
    
    impl Input {
        pub fn validate(&self, value: &str) -> ValidationResult {
            let mut errors = Vec::new();
            
            for rule in &self.props.validation_rules {
                match rule {
                    ValidationRule::Required => {
                        if value.trim().is_empty() {
                            errors.push("This field is required".to_string());
                        }
                    }
                    ValidationRule::Email => {
                        if !value.contains('@') {
                            errors.push("Please enter a valid email address".to_string());
                        }
                    }
                    ValidationRule::MinLength(min) => {
                        if value.len() < *min {
                            errors.push(format!("Minimum length is {} characters", min));
                        }
                    }
                    ValidationRule::MaxLength(max) => {
                        if value.len() > *max {
                            errors.push(format!("Maximum length is {} characters", max));
                        }
                    }
                    ValidationRule::Pattern(pattern) => {
                        let regex = regex::Regex::new(pattern).unwrap();
                        if !regex.is_match(value) {
                            errors.push("Invalid format".to_string());
                        }
                    }
                }
            }
            
            ValidationResult {
                is_valid: errors.is_empty(),
                errors,
            }
        }
    }
}

Day 5: Implement Accessibility Features

Current Problem: Missing ARIA attributes and accessibility features

Target Implementation:

impl Input {
    fn render(&self) -> impl IntoView {
        let props = &self.props;
        
        view! {
            <input
                type=props.input_type.unwrap_or(InputType::Text)
                value=props.value.unwrap_or_default()
                placeholder=props.placeholder.unwrap_or_default()
                disabled=props.disabled.unwrap_or(false)
                readonly=props.readonly.unwrap_or(false)
                required=props.required.unwrap_or(false)
                class=self.get_css_class()
                id=props.id.unwrap_or_default()
                name=props.name.unwrap_or_default()
                aria-label=props.aria_label.unwrap_or_default()
                aria-describedby=props.aria_describedby.unwrap_or_default()
                aria-invalid=props.aria_invalid.unwrap_or(false)
                aria-required=props.aria_required.unwrap_or(false)
                on:input=props.on_input
                on:change=props.on_change
                on:focus=props.on_focus
                on:blur=props.on_blur
            />
        }
    }
}

Phase 2: Test Implementation (Week 2)

Day 1-2: Basic Rendering Tests

File: packages/leptos/input/src/tests/basic_rendering.rs

#[cfg(test)]
mod basic_rendering {
    use crate::default::Input;
    use crate::InputProps;
    use leptos::prelude::*;

    #[test]
    fn test_input_renders_without_errors() {
        let input = Input::new(InputProps::default());
        let rendered = input.render();
        assert!(rendered.contains("input"));
    }

    #[test]
    fn test_input_with_placeholder() {
        let props = InputProps {
            placeholder: "Enter text".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("Enter text"));
    }

    #[test]
    fn test_input_with_value() {
        let props = InputProps {
            value: "Test value".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("Test value"));
    }

    #[test]
    fn test_input_disabled_state() {
        let props = InputProps {
            disabled: true,
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("disabled"));
    }

    #[test]
    fn test_input_required_state() {
        let props = InputProps {
            required: true,
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("required"));
    }
}

Day 3-4: Validation Tests

File: packages/leptos/input/src/tests/validation.rs

#[cfg(test)]
mod validation {
    use crate::default::Input;
    use crate::InputProps;
    use crate::validation::{ValidationRule, ValidationResult};

    #[test]
    fn test_required_validation() {
        let props = InputProps {
            validation_rules: vec![ValidationRule::Required],
            value: "".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let result = input.validate("");
        assert!(!result.is_valid);
        assert!(result.errors.iter().any(|e| e.contains("required")));
    }

    #[test]
    fn test_email_validation() {
        let props = InputProps {
            validation_rules: vec![ValidationRule::Email],
            value: "invalid-email".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let result = input.validate("invalid-email");
        assert!(!result.is_valid);
        assert!(result.errors.iter().any(|e| e.contains("email")));
    }

    #[test]
    fn test_min_length_validation() {
        let props = InputProps {
            validation_rules: vec![ValidationRule::MinLength(5)],
            value: "abc".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let result = input.validate("abc");
        assert!(!result.is_valid);
        assert!(result.errors.iter().any(|e| e.contains("Minimum length")));
    }

    #[test]
    fn test_max_length_validation() {
        let props = InputProps {
            validation_rules: vec![ValidationRule::MaxLength(10)],
            value: "very long text".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let result = input.validate("very long text");
        assert!(!result.is_valid);
        assert!(result.errors.iter().any(|e| e.contains("Maximum length")));
    }

    #[test]
    fn test_pattern_validation() {
        let props = InputProps {
            validation_rules: vec![ValidationRule::Pattern(r"^\d+$".to_string())],
            value: "abc123".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let result = input.validate("abc123");
        assert!(!result.is_valid);
        assert!(result.errors.iter().any(|e| e.contains("Invalid format")));
    }
}

Day 5: Accessibility Tests

File: packages/leptos/input/src/tests/accessibility.rs

#[cfg(test)]
mod accessibility {
    use crate::default::Input;
    use crate::InputProps;

    #[test]
    fn test_aria_label() {
        let props = InputProps {
            aria_label: "Email address".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("aria-label=\"Email address\""));
    }

    #[test]
    fn test_aria_required() {
        let props = InputProps {
            aria_required: true,
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("aria-required=\"true\""));
    }

    #[test]
    fn test_aria_invalid() {
        let props = InputProps {
            aria_invalid: true,
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("aria-invalid=\"true\""));
    }

    #[test]
    fn test_aria_describedby() {
        let props = InputProps {
            aria_describedby: "error-message".to_string(),
            ..Default::default()
        };
        let input = Input::new(props);
        let rendered = input.render();
        assert!(rendered.contains("aria-describedby=\"error-message\""));
    }
}

Phase 3: File Refactoring (Week 3)

Split Large Test Files

Current Structure:

packages/leptos/input/src/tdd_tests/
├── basic_rendering_tests.rs (224 lines) ❌
├── validation_tests.rs (180+ lines) ❌
├── accessibility_tests.rs (150+ lines) ❌
└── performance_tests.rs (140+ lines) ❌

Target Structure:

packages/leptos/input/src/tests/
├── mod.rs (20 lines)
├── basic_rendering.rs (80 lines)
├── validation.rs (90 lines)
├── accessibility.rs (85 lines)
├── performance.rs (80 lines)
└── integration.rs (90 lines)

Refactor Component Implementation

Current Structure:

packages/leptos/input/src/
├── default.rs (100+ lines) ❌
├── new_york.rs (80+ lines) ❌
└── validation.rs (stub) ❌

Target Structure:

packages/leptos/input/src/
├── default/
│   ├── mod.rs (20 lines)
│   ├── component.rs (80 lines)
│   ├── props.rs (60 lines)
│   └── styles.rs (50 lines)
├── new_york/
│   ├── mod.rs (20 lines)
│   ├── component.rs (80 lines)
│   └── styles.rs (50 lines)
└── validation/
    ├── mod.rs (20 lines)
    ├── rules.rs (80 lines)
    ├── result.rs (60 lines)
    └── context.rs (50 lines)

📋 Implementation Checklist

Week 1: Emergency Fixes

  • Day 1: Remove all stub test implementations
  • Day 2: Implement basic rendering tests
  • Day 3: Create validation system
  • Day 4: Implement validation tests
  • Day 5: Add accessibility features and tests

Week 2: Test Implementation

  • Day 1: Implement basic rendering tests
  • Day 2: Implement validation tests
  • Day 3: Implement accessibility tests
  • Day 4: Implement performance tests
  • Day 5: Implement integration tests

Week 3: File Refactoring

  • Day 1: Split large test files
  • Day 2: Refactor component implementation
  • Day 3: Create modular structure
  • Day 4: Update documentation
  • Day 5: Validate all files under 300 lines

Week 4: Validation & Documentation

  • Day 1: Run full test suite
  • Day 2: Performance benchmarking
  • Day 3: Accessibility testing
  • Day 4: Update documentation
  • Day 5: Final validation and sign-off

🎯 Success Metrics

Week 1 Targets

  • Zero stub tests remaining
  • Basic functionality working
  • Validation system implemented
  • Accessibility features added

Week 2 Targets

  • 90% test coverage achieved
  • All test categories implemented
  • Performance benchmarks established
  • Integration tests working

Week 3 Targets

  • All files under 300 lines
  • Modular structure implemented
  • Clean code organization
  • Maintainable architecture

Week 4 Targets

  • 100% test coverage
  • AAA accessibility compliance
  • Performance targets met
  • Production ready

🚨 Risk Mitigation

High-Risk Areas

  1. Validation System: Complex logic, high chance of bugs
  2. Accessibility: WCAG compliance requirements
  3. Performance: Real-time validation impact
  4. File Refactoring: Breaking existing functionality

Mitigation Strategies

  1. Incremental Implementation: Small, testable changes
  2. Comprehensive Testing: Test-driven development
  3. Code Reviews: Peer review for all changes
  4. Rollback Plans: Git branches for each phase

🔧 Tools & Dependencies

Required Dependencies

[dev-dependencies]
regex = "1.10"
proptest = "1.4"
criterion = "0.5"
wasm-bindgen-test = "0.3"

Testing Tools

  • Unit Testing: Rust native testing
  • Property Testing: Proptest
  • Performance Testing: Criterion
  • WASM Testing: wasm-bindgen-test

🚀 Next Steps

  1. Start immediately with stub test removal
  2. Implement validation system as priority
  3. Add accessibility features for compliance
  4. Refactor large files for maintainability
  5. Validate production readiness before completion

This remediation plan will transform the Input component from a broken, stub-filled mess into a production-ready, fully-tested component within 4 weeks.


Remediation plan created: September 20, 2025
Target completion: October 18, 2025