# Conditional Fields Design **Dynamic Field Visibility** - Fields that appear/disappear based on other field values in SOP templates. ## Overview Conditional fields enable dynamic form behavior where field visibility depends on the values of other fields. For example, "Show reagent volume only if experiment type = titration" creates more intuitive and streamlined SOP templates. ## Use Cases ### Laboratory Workflow Examples - **Experiment Type Branching**: Show specific fields based on selected experiment type - **Equipment Selection**: Display relevant parameters based on chosen instrument - **Safety Protocols**: Show additional safety fields for hazardous procedures - **Sample Types**: Adjust required fields based on sample characteristics ## Implementation Strategy ### Phase-Based Development #### Phase 1: Same-Task Conditionals - **Scope**: Fields within the same task can depend on each other - **Complexity**: Low - single task context - **Implementation**: Direct field references within task scope #### Phase 2: Cross-Task References - **Scope**: Fields can depend on fields from other tasks - **Complexity**: Medium - cross-task field resolution - **Implementation**: Task-qualified field paths #### Phase 3: Complex Logic - **Scope**: AND/OR combinations, multiple conditions - **Complexity**: High - complex evaluation engine - **Implementation**: Expression parser and evaluator ## Technical Architecture ### Data Structure ```typescript interface ConditionalField extends Field { conditions: Array<{ id: string; targetField: string; // "task_id.field_name" or "field_name" operator: ConditionOperator; value: any; logicalOperator?: 'AND' | 'OR'; }>; showWhen: boolean; // Show when conditions are true/false } type ConditionOperator = | 'equals' | 'not_equals' | 'greater_than' | 'less_than' | 'contains' | 'not_contains' | 'is_empty' | 'is_not_empty'; ``` ### Field Reference Resolution ```typescript interface FieldReference { taskId?: string; // Optional for cross-task references fieldName: string; fieldType: 'string' | 'number' | 'boolean' | 'date' | 'enum'; currentValue: any; } class FieldResolver { resolveFieldPath(path: string, sopData: SOPData): FieldReference validateFieldType(field: FieldReference, operator: ConditionOperator): boolean evaluateCondition(condition: Condition, sopData: SOPData): boolean } ``` ## UI Components ### Condition Builder Interface ``` ┌─ Make Field Conditional? ──────────────────────┐ │ ☑ Show this field conditionally │ │ │ │ Show field when: │ │ ┌─ Condition Builder ─────────────────────────┐│ │ │ Field: [Task Dropdown▼] [Field Dropdown▼] ││ │ │ Operator: [equals▼] Value: [titration____] ││ │ │ ⊕ Add another condition (AND/OR) ││ │ └─────────────────────────────────────────────┘│ │ │ │ Preview: Field is currently [hidden/shown] │ └────────────────────────────────────────────────┘ ``` ### Dynamic Form Behavior ```typescript const ConditionalFieldRenderer: React.FC<{ field: ConditionalField; sopData: SOPData; onChange: (fieldName: string, value: any) => void; }> = ({ field, sopData, onChange }) => { const isVisible = useMemo(() => evaluateFieldConditions(field.conditions, sopData), [field.conditions, sopData] ); if (!isVisible) return null; return ; }; ``` ## Validation & Error Handling ### Circular Dependency Detection ```typescript class DependencyAnalyzer { detectCircularDependencies(fields: ConditionalField[]): string[] { const graph = this.buildDependencyGraph(fields); return this.findCycles(graph); } private buildDependencyGraph(fields: ConditionalField[]): DependencyGraph { // Build directed graph of field dependencies } private findCycles(graph: DependencyGraph): string[] { // Detect circular references using DFS } } ``` ### Runtime Validation ```typescript interface ValidationResult { isValid: boolean; errors: Array<{ fieldId: string; message: string; type: 'circular_dependency' | 'invalid_reference' | 'type_mismatch'; }>; } class ConditionalFieldValidator { validateConditions(field: ConditionalField, availableFields: Field[]): ValidationResult validateOperatorCompatibility(fieldType: string, operator: ConditionOperator): boolean validateValueType(value: any, fieldType: string, operator: ConditionOperator): boolean } ``` ## Performance Considerations ### Evaluation Optimization ```typescript class ConditionEvaluator { private memoizedResults = new Map(); evaluateWithMemoization(conditions: Condition[], sopData: SOPData): boolean { const key = this.generateCacheKey(conditions, sopData); if (this.memoizedResults.has(key)) { return this.memoizedResults.get(key)!; } const result = this.evaluate(conditions, sopData); this.memoizedResults.set(key, result); return result; } clearCache(): void { this.memoizedResults.clear(); } } ``` ### Change Detection ```typescript const useConditionalFields = (fields: ConditionalField[], sopData: SOPData) => { const [visibilityMap, setVisibilityMap] = useState>({}); // Only re-evaluate when relevant fields change const relevantData = useMemo(() => { const relevantFields = extractRelevantFields(fields); return pick(sopData, relevantFields); }, [fields, sopData]); useEffect(() => { const newVisibility = evaluateAllConditions(fields, relevantData); setVisibilityMap(newVisibility); }, [fields, relevantData]); return visibilityMap; }; ``` ## Schema Integration ### Schema Definition Updates ```yaml # Add to SOP Template Schema ConditionalField: allOf: - $ref: '#/definitions/Field' - type: object properties: conditions: type: array items: type: object properties: id: { type: string } targetField: { type: string } operator: type: string enum: ['equals', 'not_equals', 'greater_than', 'less_than', 'contains'] value: { } logicalOperator: type: string enum: ['AND', 'OR'] showWhen: type: boolean default: true ``` ### Registry Integration ```typescript class SchemaRegistry { // Add conditional field support getConditionalFields(taskId: string): ConditionalField[] resolveFieldDependencies(fieldId: string): string[] validateConditionalLogic(fields: ConditionalField[]): ValidationResult } ``` ## Implementation Roadmap ### Phase 1 (Same-Task Conditionals) 1. **Data Structure**: Define conditional field schema 2. **UI Builder**: Create condition builder interface 3. **Evaluation Engine**: Simple within-task evaluation 4. **Form Integration**: Update form renderer for conditionals 5. **Validation**: Basic validation and error handling ### Phase 2 (Cross-Task References) 1. **Path Resolution**: Implement task-qualified field paths 2. **Dependency Tracking**: Cross-task dependency analysis 3. **UI Enhancement**: Task/field selection in condition builder 4. **Performance**: Optimize cross-task evaluation 5. **Testing**: Comprehensive cross-task scenarios ### Phase 3 (Complex Logic) 1. **Expression Parser**: AND/OR logic evaluation 2. **Advanced UI**: Complex condition builder interface 3. **Optimization**: Advanced memoization and caching 4. **Migration Tools**: Convert simple to complex conditionals 5. **Documentation**: User guides and examples ## User Experience ### Design Principles - **Progressive Disclosure**: Start simple, add complexity as needed - **Visual Feedback**: Clear indication of conditional relationships - **Error Prevention**: Guide users to valid configurations - **Real-time Preview**: Show form behavior as conditions are built ### Accessibility - **Screen Readers**: Announce visibility changes - **Keyboard Navigation**: Full keyboard accessibility - **Visual Indicators**: Clear visual cues for conditional fields - **Focus Management**: Proper focus handling for dynamic fields ## Testing Strategy ### Unit Tests - Condition evaluation logic - Circular dependency detection - Field reference resolution - Type validation ### Integration Tests - Form rendering with conditionals - Cross-task field resolution - Schema registry integration - Performance with large forms ### E2E Tests - Complete user workflows - Complex conditional scenarios - Error handling and recovery - Accessibility compliance