feat(champ.errors): add aria-labelledby error on champs wrapped within a fieldset, make error messages always present and aria-live: assertive on validation error

Co-authored-by: Corinne Durrmeyer <corinne@inseo.fr>
Co-authored-by: Colin Darie <colin@darie.eu>
This commit is contained in:
mfo 2024-04-09 16:19:54 +02:00
parent 331dfd3044
commit 8453e121d7
No known key found for this signature in database
GPG key ID: 7CE3E1F5B794A8EC
6 changed files with 14 additions and 11 deletions

View file

@ -34,6 +34,14 @@ module Dsfr
errors.full_messages_for(attribute_or_rich_body) errors.full_messages_for(attribute_or_rich_body)
end end
def fieldset_error_opts
if dsfr_champ_container == :fieldset && errors_on_attribute?
{ aria: { labelledby: "#{describedby_id} #{object.labelledby_id}" } }
else
{}
end
end
private private
# lookup for edge case from `form.rich_text_area` # lookup for edge case from `form.rich_text_area`
@ -73,7 +81,6 @@ module Dsfr
'fr-input': true, 'fr-input': true,
'fr-mb-0': true 'fr-mb-0': true
}.merge(input_error_class_names))) }.merge(input_error_class_names)))
if errors_on_attribute? if errors_on_attribute?
@opts.deep_merge!(aria: { @opts.deep_merge!(aria: {
describedby: describedby_id describedby: describedby_id

View file

@ -1,14 +1,10 @@
module Dsfr module Dsfr
class InputStatusMessageComponent < ApplicationComponent class InputStatusMessageComponent < ApplicationComponent
def initialize(errors_on_attribute:, error_full_messages:, described_by:, champ:) def initialize(errors_on_attribute:, error_full_messages:, describedby_id:, champ:)
@errors_on_attribute = errors_on_attribute @errors_on_attribute = errors_on_attribute
@error_full_messages = error_full_messages @error_full_messages = error_full_messages
@described_by = described_by @describedby_id = describedby_id
@champ = champ @champ = champ
end end
def render?
@errors_on_attribute
end
end end
end end

View file

@ -1,4 +1,4 @@
.fr-messages-group{ id: @describedby_id } .fr-messages-group{ id: @describedby_id, aria: { live: :assertive } }
- if @error_full_messages.size > 0 - if @error_full_messages.size > 0
%p{ class: class_names('fr-message' => true, "fr-message--#{@errors_on_attribute ? 'error' : 'valid'}" => true) } %p{ class: class_names('fr-message' => true, "fr-message--#{@errors_on_attribute ? 'error' : 'valid'}" => true) }
= "« #{@champ.libelle} » " = "« #{@champ.libelle} » "

View file

@ -34,7 +34,7 @@ class EditableChamp::EditableChampComponent < ApplicationComponent
}.merge(champ_component.input_group_error_class_names) }.merge(champ_component.input_group_error_class_names)
), ),
data: { controller: stimulus_controller, **data_dependent_conditions, **stimulus_values } data: { controller: stimulus_controller, **data_dependent_conditions, **stimulus_values }
} }.deep_merge(champ_component.fieldset_error_opts)
end end
def fieldset_element_attributes def fieldset_element_attributes

View file

@ -5,6 +5,6 @@
= render champ_component = render champ_component
= render Dsfr::InputStatusMessageComponent.new(errors_on_attribute: champ_component.errors_on_attribute?, error_full_messages: champ_component.error_full_messages, described_by: @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, describedby_id: @champ.describedby_id, champ: @champ)
= @form.hidden_field :id, value: @champ.id = @form.hidden_field :id, value: @champ.id

View file

@ -188,7 +188,7 @@ class Champ < ApplicationRecord
end end
def describedby_id def describedby_id
"#{html_id}-description" if description.present? "#{html_id}-describedby_id"
end end
def log_fetch_external_data_exception(exception) def log_fetch_external_data_exception(exception)