ViewComponents - Passe Marché
This directory contains ViewComponent-based components for the Passe Marché application. ViewComponents provide a modern, testable, and maintainable approach to building reusable view logic.
What is ViewComponent?
ViewComponent is a framework for building reusable, testable & encapsulated view components in Ruby on Rails. It's an alternative to traditional Rails partials with several advantages:
Testable: Components can be unit tested in isolation
Encapsulated: Logic and templates are co-located
Performance: Components are faster than partials (10-100x in some cases)
Type Safety: Ruby classes provide better IDE support and refactoring
Documentation: Lookbook provides living documentation with previews
Directory Structure
app/components/
├── market_attribute_response/
│ ├── base_component.rb # Shared base class
│ ├── fields/ # Reusable field components
│ │ ├── file_upload_field_component.rb
│ │ └── textarea_field_component.rb
│ ├── shared/ # Shared utility components
│ │ ├── document_item_component.rb
│ │ └── progress_bar_component.rb
│ │
│ │ # Simple Input Components
│ ├── text_input_component.rb # Text input field
│ ├── textarea_component.rb # Multi-line text area
│ ├── email_input_component.rb # Email input field
│ ├── phone_input_component.rb # Phone number input
│ │
│ │ # File Upload Components
│ ├── file_upload_component.rb # Standard file upload
│ ├── inline_file_upload_component.rb # Inline file upload (auto-fillable)
│ ├── inline_url_input_component.rb # URL input field
│ │
│ │ # Composite Input Components
│ ├── file_or_textarea_component.rb # File OR text choice
│ ├── checkbox_with_document_component.rb # Checkbox with document upload
│ ├── radio_with_file_and_text_component.rb # Radio with file/text options
│ │
│ │ # Financial/Workforce Components
│ ├── capacite_economique_financiere_chiffre_affaires_global_annuel_component.rb
│ ├── capacite_economique_financiere_effectifs_moyens_annuels_component.rb
│ │
│ │ # Complex Repeatable Components
│ ├── capacites_techniques_professionnelles_outillage_echantillons_component.rb
│ ├── presentation_intervenants_component.rb
│ └── realisations_livraisons_component.rb
│
├── source_badge_component.rb # Badge showing data source
└── source_badge_component.html.erb
app/views/market_attribute_response/{component_name}_component/
├── _form.html.erb # Form mode sub-template
├── _display.html.erb # Display mode sub-template
└── _{nested}.html.erb # Optional nested field templates (for repeatable components)
spec/components/
├── market_attribute_response/
│ ├── base_component_spec.rb
│ ├── fields/
│ │ └── {field}_component_spec.rb
│ ├── shared/
│ │ └── {shared}_component_spec.rb
│ └── {component}_component_spec.rb
└── source_badge_component_spec.rb
spec/components/previews/
└── market_attribute_response/
└── {component}_component_preview.rb # Lookbook previewsUsing Components
In Views
Components are automatically used via dynamic resolution in the main views. The system tries to load a component first, then falls back to the old partial if the component doesn't exist:
Direct Usage
You can also render components directly:
Component Architecture
BaseComponent
All MarketAttributeResponse components inherit from MarketAttributeResponse::BaseComponent, which provides:
Mode Detection:
form_mode?,display_mode?Source Helpers:
manual?,auto?,manual_after_api_failure?Visibility Logic:
show_value?(context-aware)I18n Helpers:
field_label,field_description,auto_filled_messageAttribute Access:
market_attributedelegate
Component Parameters
Components accept these parameters:
Form Mode vs Display Mode
Form Mode (when form is present):
Uses
form.fields_for :market_attribute_responsesfor nested attributesRenders input fields or hidden fields (for auto-filled data)
Shows validation errors
Includes required indicators
Display Mode (when form is nil):
Shows read-only field values
Respects context for visibility (:web/:pdf hide auto values, :buyer shows all)
Renders source badges
Uses custom layout classes for PDF generation
Context-Aware Rendering
The :context parameter controls visibility:
:web- Candidate web view (summary page)Hides auto-filled values for privacy
Shows manual values
:pdf- Candidate attestation PDFHides auto-filled values for privacy
Shows manual values
Uses PDF-specific layout classes
:buyer- Buyer's ZIP packageShows ALL values (including auto-filled)
Buyer needs complete information
Testing Components
Unit Tests (RSpec)
Components have dedicated RSpec tests using ViewComponent test helpers:
Integration Tests (Cucumber)
Components are tested end-to-end through Cucumber features that test the full candidate flow.
Lookbook Previews
Lookbook provides a UI for viewing and documenting components. Access it at:
Development: http://localhost:3000/lookbook
Sandbox: http://sandbox-url/lookbook
Production: Not accessible (environment-restricted)
Creating Previews
Previews live in spec/components/previews/:
Creating New Components
1. Create Component Class
2. Create Component Template
3. Create Sub-templates
Form template (app/views/market_attribute_response/your_type_component/_form.html.erb):
Display template (app/views/market_attribute_response/your_type_component/_display.html.erb):
4. Create Component Spec
5. Create Lookbook Preview
6. Delete Old Partials
After testing, remove the old partials:
Best Practices
DO:
✅ Inherit from
BaseComponentfor MarketAttributeResponse components✅ Use
fields_forfor nested attributes in form mode✅ Test components in isolation with RSpec
✅ Create Lookbook previews for visual documentation
✅ Use context parameter for visibility control
✅ Follow DSFR CSS classes (
fr-*)✅ Keep custom classes for PDF layout (
field-layout-container)✅ Extract complex logic to component methods
DON'T:
❌ Put business logic in components (use services/models)
❌ Access instance variables directly (use parameters)
❌ Skip the
fields_forwrapper in forms❌ Forget to update component specs when changing templates
❌ Hardcode labels (use i18n helpers)
❌ Mix DSFR classes with custom layout classes
CSS Classes Reference
DSFR (French Design System)
fr-input,fr-input-group,fr-input--errorfr-label,fr-label--requiredfr-hint-text,fr-error-textfr-text--sm,fr-text--md,fr-text--mention-greyfr-badge,fr-badge--success,fr-badge--warningfr-mb-2w,fr-mb-4w
Custom (PDF Generation)
field-layout-container- Container for field displayfield-layout-content- Content wrapper for PDF
Running Tests
Migration Status
All market attribute response partials have been migrated to ViewComponents:
Shared
DocumentItemComponent, ProgressBarComponent
✅ Complete
Fields
FileUploadFieldComponent, TextareaFieldComponent
✅ Complete
Simple Inputs
TextInput, Textarea, Email, Phone
✅ Complete
File Uploads
FileUpload, InlineFileUpload, InlineUrlInput
✅ Complete
Composite
FileOrTextarea, CheckboxWithDocument, RadioWithFileAndText
✅ Complete
Financial
ChiffreAffairesGlobalAnnuel, EffectifsMoyensAnnuels
✅ Complete
Repeatable
OutillageEchantillons, PresentationIntervenants, RealisationsLivraisons
✅ Complete
Legacy partials removed:
All
_*_form.html.erbpartialsAll
_*_display.html.erbpartialsshared/_progress_bar.html.erb,shared/_document_item.html.erbfields/_file_upload_field.html.erb,fields/_textarea_field.html.erb_source_badge.html.erb
Additional Resources
Questions?
Check the component specs or reach out to the development team.
Mis à jour
Ce contenu vous a-t-il été utile ?

