Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ETQ usager, je veux que les erreurs et les descriptions soient reliées aux champs concernés #10991

Merged
merged 18 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions app/assets/stylesheets/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,34 @@
}
}

.fr-label .fr-hint-text > *,
.fr-fieldset__legend .fr-hint-text > * {
.fr-label + .fr-hint-text > *,
.fr-fieldset__legend + .fr-hint-text > * {
// la description d'un champ peut contenir du markup (markdown->html),
// on herite donc la fontsize/mrgin/padding du fr-hint-text
// on herite donc la taille de police, la couleur et l'interlignage de fr-hint-text
// on réinitialise les marges des éléments
font-size: inherit;
margin: inherit;
padding: inherit;
line-height: inherit;
color: inherit;
margin: 0;
padding: 0;
}

.fr-fieldset .fr-hint-text ul {
margin-left: 0.75rem;
}

.fr-fieldset .fr-hint-text ol {
margin-left: 1.25rem;
}

.fr-hint-text + .fr-input {
// les champs se trouvant juste après un message d'aide ont le même espacement que ceux se trouvant juste après un label
margin-top: 0.5rem;
}

.fr-fieldset__legend .fr-hint-text {
// les messages d'aide se trouvant dans une légende ont le même espacement que ceux se trouvant dans un label
margin-top: 0.25rem;
}

input[type='password'],
Expand Down Expand Up @@ -389,10 +410,6 @@
}
}

.editable-champ-titre_identite {
margin-bottom: 2 * $default-padding;
}

.cnaf-inputs,
.dgfip-inputs,
.pole-emploi-inputs,
Expand Down
16 changes: 15 additions & 1 deletion app/components/attachment/edit_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,30 @@ def attachment_input_class
"attachment-input-#{attachment_id}"
end

def show_hint?
first? && !persisted?
end

def file_field_options
track_issue_with_missing_validators if missing_validators?

options = {
class: class_names("fr-upload attachment-input": true, "#{attachment_input_class}": true),
direct_upload: @direct_upload,
id: input_id,
aria: { describedby: champ&.describedby_id },
data: {
auto_attach_url:,
turbo_force: :server
}.merge(has_file_size_validator? ? { max_file_size: max_file_size } : {})
}

describedby = []
describedby << champ.describedby_id if champ&.description.present?
describedby << describedby_hint_id if show_hint?
describedby << champ.error_id if champ&.errors&.has_key?(:value)

options[:aria] = { describedby: describedby.join(' ') }

options.merge!(has_content_type_validator? ? { accept: accept_content_type } : {})
options[:multiple] = true if as_multiple?
options[:disabled] = true if (@max && @index >= @max) || persisted?
Expand Down Expand Up @@ -169,6 +179,10 @@ def error_message(attachment)

private

def describedby_hint_id
"#{input_id}-pj-hint"
end

def input_id
if champ.present?
# There is always a single input by champ, its id must match the label "for" attribute.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
- if error?(attachment)
%p.fr-error-text= error_message(attachment)

- if first? && !persisted?
%p.fr-hint-text.fr-mb-1w
- if show_hint?
%p.fr-hint-text.fr-mb-1w{ id: describedby_hint_id }
- if max_file_size.present?
= t('.max_file_size', max_file_size: number_to_human_size(max_file_size))
- if allowed_formats.present?
Expand Down
29 changes: 17 additions & 12 deletions app/components/dsfr/input_errorable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ def error_full_messages

def fieldset_error_opts
if dsfr_champ_container == :fieldset && errors_on_attribute?
{ aria: { labelledby: "#{describedby_id} #{object.labelledby_id}" } }
labelledby = [@champ.labelledby_id]
labelledby << describedby_id if @champ.description.present?
labelledby << @champ.error_id

{
aria: { labelledby: labelledby.join(' ') }
}
else
{}
end
Expand Down Expand Up @@ -67,14 +73,6 @@ def input_error_class_names
}
end

def input_error_opts
{
aria: {
describedby: describedby_id
}
}
end

def react_input_opts(other_opts = {})
input_opts(other_opts, true)
end
Expand All @@ -87,12 +85,19 @@ def input_opts(other_opts = {}, react = false)
'fr-input': !react,
'fr-mb-0': true
}.merge(input_error_class_names)))
if errors_on_attribute?
@opts.deep_merge!('aria-describedby': describedby_id)

aria_describedby = []

if object.respond_to?(:description) && object.description.present?
aria_describedby << describedby_id
elsif hintable?
@opts.deep_merge!('aria-describedby': hint_id)
aria_describedby << hint_id
end

aria_describedby << object.error_id if errors_on_attribute? && object.respond_to?(:error_id)

@opts.deep_merge!('aria-describedby': aria_describedby.join(' ')) if aria_describedby.present?

if @required
@opts[react ? :is_required : :required] = true
end
Expand Down
4 changes: 2 additions & 2 deletions app/components/dsfr/input_status_message_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

module Dsfr
class InputStatusMessageComponent < ApplicationComponent
def initialize(errors_on_attribute:, error_full_messages:, describedby_id:, champ:)
def initialize(errors_on_attribute:, error_full_messages:, champ:)
@errors_on_attribute = errors_on_attribute
@error_full_messages = error_full_messages
@describedby_id = describedby_id
@error_id = champ.error_id
@champ = champ
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.fr-messages-group{ id: @describedby_id }
.fr-messages-group{ id: @error_id, aria: { live: :assertive } }
- if @error_full_messages.size > 0
%p{ class: class_names('fr-message' => true, "fr-message--#{@errors_on_attribute ? 'error' : 'valid'}" => true) }
= "« #{@champ.libelle} » "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
- if @champ.html_label?
= @form.label @champ.main_value_name, id: @champ.labelledby_id, for: @champ.input_id, class: 'fr-label' do
- render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at
- if @champ.description.present?
.fr-hint-text{ id: @champ.describedby_id }= render SimpleFormatComponent.new(@champ.description, allow_a: true)
- elsif @champ.legend_label?
%legend.fr-fieldset__legend.fr-text--regular{ id: @champ.labelledby_id }= render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at
%legend.fr-fieldset__legend.fr-text--regular.fr-pb-1w{ id: @champ.labelledby_id }= render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at
- if @champ.description.present?
.fr-hint-text.fr-mt-n1w.fr-mb-1w.fr-pl-1w.width-100{ id: @champ.describedby_id }= render SimpleFormatComponent.new(@champ.description, allow_a: true)
colinux marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,5 @@
- if hint?
%span.fr-hint-text{ data: { controller: 'date-input-hint' } }= hint

- if @champ.description.present?
%span.fr-hint-text= render SimpleFormatComponent.new(@champ.description, allow_a: true)

- if @champ.textarea? && @champ.character_limit_base&.positive?
%span.fr-hint-text= t('.recommended_size', size: @champ.character_limit_base)
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
.fr-checkbox-group
.fr-checkbox-group.fr-mb-2w
= @form.check_box :value,
{ required: @champ.required?, id: @champ.input_id, checked: @champ.true?, aria: { describedby: @champ.describedby_id }, class: class_names('required' => @champ.required?)},
{ required: @champ.required?, id: @champ.input_id, checked: @champ.true?, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" } },
'true',
'false'
%label.fr-label{ for: @champ.input_id, id: @champ.labelledby_id }
%span
%span.width-100
= render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at
- if @champ.description.present?
.fr-hint-text.fr-mt-1v.fr-ml-4w{ id: @champ.describedby_id }= render SimpleFormatComponent.new(@champ.description, allow_a: true)
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
%p.notice= t('.numero_allocataire_notice')
= @form.text_field :numero_allocataire,
required: @champ.required?,
aria: { describedby: @champ.describedby_id },
aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" },
class: "width-33-desktop", id: @champ.numero_allocataire_input_id

%div
= @form.label :code_postal, t('.code_postal_label'), for: @champ.code_postal_input_id
%p.notice= t('.code_postal_notice')
= @form.text_field :code_postal,
required: @champ.required?,
aria: { describedby: @champ.describedby_id },
aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" },
class: "width-33-desktop",
id: @champ.code_postal_input_id
6 changes: 6 additions & 0 deletions app/components/editable_champ/communes_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
class EditableChamp::CommunesComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper

def call
tag.react_fragment do
render(ReactComponent.new("ComboBox/RemoteComboBox", **react_props))
end
end

def dsfr_input_classname
'fr-select'
end
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= @form.date_field :value,
input_opts(id: @champ.input_id,
aria: { describedby: @champ.describedby_id },
aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" },
value: @champ.value,
required: @champ.required?,
class: "width-33-desktop")
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.datetime_field(:value, input_opts(value: formatted_value_for_datetime_locale, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, data: { controller: 'datetime' }))
= @form.datetime_field(:value, input_opts(value: formatted_value_for_datetime_locale, id: @champ.input_id, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }, data: { controller: 'datetime' }))
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, required: @champ.required?, pattern: "-?[0-9]+([\.,][0-9]{1,3})?", inputmode: :decimal, data: { controller: 'format', format: :decimal }))
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }, required: @champ.required?, pattern: "-?[0-9]+([\.,][0-9]{1,3})?", inputmode: :decimal, data: { controller: 'format', format: :decimal }))
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "fr-select"
= @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }, class: "fr-select"
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
%p.notice= t('.numero_fiscal_notice')
= @form.text_field :numero_fiscal,
required: @champ.required?,
aria: { describedby: @champ.describedby_id },
aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" },
class: "width-33-desktop", id: @champ.numero_fiscal_input_id

%div
= @form.label :reference_avis, t('.reference_avis_label'), for: @champ.reference_avis_input_id
%p.notice= t('.reference_avis_notice')
= @form.text_field :reference_avis,
required: @champ.required?,
aria: { describedby: @champ.describedby_id },
aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" },
class: "width-33-desktop", id: @champ.reference_avis_input_id
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, inputmode: :numeric, min: 1, pattern: "[0-9]{1,12}", autocomplete: 'off', required: @champ.required?, class: "width-33-desktop #{@champ.blank? ? '' : 'small-margin'}"))
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }, inputmode: :numeric, min: 1, pattern: "[0-9]{1,12}", autocomplete: 'off', required: @champ.required?, class: "width-33-desktop #{@champ.blank? ? '' : 'small-margin'}"))

- if [email protected]? && !dossier.blank?
.fr-info-text.fr-mb-4w= sanitize(dossier.text_summary)
15 changes: 11 additions & 4 deletions app/components/editable_champ/drop_down_list_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,21 @@ def contains_long_option?
@champ.drop_down_options.any? { _1.size > max_length }
end

def select_aria_describedby
describedby = []
describedby << @champ.describedby_id if @champ.description.present?
describedby << @champ.error_id if errors_on_attribute?
describedby.present? ? describedby.join(' ') : nil
end

def react_props
react_input_opts(id: @champ.input_id,
react_input_opts(
id: @champ.input_id,
class: 'fr-mt-1w',
name: @form.field_name(:value),
selected_key: @champ.selected,
items: @champ.drop_down_options_with_other.map { _1.is_a?(Array) ? _1 : [_1, _1] },
empty_filter_key: @champ.drop_down_other? ? Champs::DropDownListChamp::OTHER : nil,
'aria-describedby': @champ.describedby_id,
'aria-labelledby': @champ.labelledby_id)
empty_filter_key: @champ.drop_down_other? ? Champs::DropDownListChamp::OTHER : nil
)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
= @form.select :value,
@champ.drop_down_options_with_other,
{ selected: @champ.selected, include_blank: true },
required: @champ.required?,
{ required: @champ.required?,
id: @champ.input_id,
class: select_class_names,
aria: { describedby: @champ.describedby_id }
aria: { describedby: select_aria_describedby } }

- if @champ.drop_down_other?
%div{ class: other_element_class_names }
Expand Down
14 changes: 14 additions & 0 deletions app/components/editable_champ/editable_champ_base_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,18 @@ def dsfr_input_classname
def describedby_id
@champ.describedby_id
end

def fieldset_aria_opts
if dsfr_champ_container == :fieldset
labelledby = [@champ.labelledby_id]
labelledby << describedby_id if @champ.description.present?

{
aria: { labelledby: labelledby.join(' ') },
role: 'group'
}
else
{}
end
end
end
4 changes: 3 additions & 1 deletion app/components/editable_champ/editable_champ_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ def html_options
}.merge(champ_component.input_group_error_class_names)
),
data: { controller: stimulus_controller, **data_dependent_conditions, **stimulus_values }
}.deep_merge(champ_component.fieldset_error_opts)
}
.deep_merge(champ_component.fieldset_aria_opts)
.deep_merge(champ_component.fieldset_error_opts)
end

def fieldset_element_attributes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

= render champ_component

= render Dsfr::InputStatusMessageComponent.new(errors_on_attribute: champ_component.errors_on_attribute?, error_full_messages: champ_component.error_full_messages, describedby_id: @champ.describedby_id, champ: @champ)
= render Dsfr::InputStatusMessageComponent.new(errors_on_attribute: champ_component.errors_on_attribute?, error_full_messages: champ_component.error_full_messages, champ: @champ)
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.email_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, required: @champ.required?))
= @form.email_field(:value, input_opts(id: @champ.input_id, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }, required: @champ.required?))
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, required: @champ.required?, aria: { describedby: @champ.describedby_id }))
= @form.text_field(:value, input_opts(id: @champ.input_id, required: @champ.required?, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }))
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
.fr-select-group
= @form.label :value, for: @champ.epci_input_id, class: 'fr-label' do
- "EPCI"
= @form.select :value, epci_options, epci_select_options, required: @champ.required?, id: @champ.epci_input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile fr-select"
= @form.select :value, epci_options, epci_select_options, required: @champ.required?, id: @champ.epci_input_id, class: "width-33-desktop width-100-mobile fr-select"
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, placeholder: @champ.expression_reguliere_exemple_text, required: @champ.required?, aria: { describedby: @champ.describedby_id }))
= @form.text_field(:value, input_opts(id: @champ.input_id, placeholder: @champ.expression_reguliere_exemple_text, required: @champ.required?, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }))
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, required: @champ.required?, aria: { describedby: @champ.describedby_id }, data: { controller: 'format', format: 'iban' }, class: "width-66-desktop", maxlength: 34 + 9)) # count space separator of 4 digits-groups
= @form.text_field(:value, input_opts(id: @champ.input_id, required: @champ.required?, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }, data: { controller: 'format', format: 'iban' }, class: "width-66-desktop", maxlength: 34 + 9)) # count space separator of 4 digits-groups
Original file line number Diff line number Diff line change
@@ -1 +1 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, pattern: "-?[0-9]*", inputmode: :numeric, required: @champ.required?, data: { controller: 'format', format: :integer }))
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }, pattern: "-?[0-9]*", inputmode: :numeric, required: @champ.required?, data: { controller: 'format', format: :integer }))
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.fr-select-group
= render EditableChamp::ChampLabelComponent.new form: @form, champ: @champ, seen_at: @seen_at

= @form.select :primary_value, @champ.primary_options, {}, required: @champ.required?, class: 'fr-select fr-mb-3v', id: @champ.input_id, aria: { describedby: @champ.describedby_id }
= @form.select :primary_value, @champ.primary_options, {}, required: @champ.required?, class: 'fr-select fr-mb-3v', id: @champ.input_id, aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" }

- if @champ.has_secondary_options_for_primary?
.secondary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
%p.notice= t('.ine_notice')
= @form.text_field :ine,
required: @champ.required?,
aria: { describedby: @champ.describedby_id },
aria: { describedby: "#{@champ.describedby_id} #{@champ.error_id}" },
class: "width-33-desktop", id: @champ.input_id
Loading
Loading