class Dsfr::InputComponent < ApplicationComponent delegate :object, to: :@form delegate :errors, to: :object # use it to indicate detailed about the inputs, ex: https://www.systeme-de-design.gouv.fr/elements-d-interface/modeles-et-blocs-fonctionnels/demande-de-mot-de-passe # it uses aria-describedby on input and link it to yielded content renders_one :describedby def initialize(form:, attribute:, input_type: :text_field, opts: {}, required: true) @form = form @attribute = attribute @input_type = input_type @opts = opts @required = required end # add invalid class on input when input is invalid # and and valid on input only if another input is invalid def input_group_opts opts = { class: class_names('fr-input-group': true, 'fr-password': password?, "fr-input-group--error": errors_on_attribute?, "fr-input-group--valid": !errors_on_attribute? && errors_on_another_attribute?) } if email? opts[:data] = { controller: 'email-input' } end opts end def label_opts { class: class_names('fr-label': true, 'fr-password__label': password?) } end def input_opts @opts[:class] = class_names(map_array_to_hash_with_true(@opts[:class]) .merge('fr-password__input': password?, 'fr-input': true, 'fr-mb-0': true, 'fr-input--error': errors_on_attribute?)) if errors_on_attribute? || describedby? @opts.deep_merge!(aria: { describedby: describedby_id, invalid: errors_on_attribute? }) end if @required @opts[:required] = true end if email? @opts.deep_merge!(data: { action: "blur->email-input#checkEmail", 'email-input-target': 'input' }) end @opts end # errors helpers def errors_on_attribute? errors.has_key?(attribute_or_rich_body) end def error_messages errors.full_messages_for(attribute_or_rich_body) end def describedby_id dom_id(object, "#{@attribute}-messages") end # i18n lookups def label object.class.human_attribute_name(@attribute) end def hint I18n.t("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}") end # kind of input helpers def password? @input_type == :password_field end def email? @input_type == :email_field end def show_password_id dom_id(object, "#{@attribute}_show_password") end private def hint? I18n.exists?("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}") end def errors_on_another_attribute? !errors.empty? end # lookup for edge case from `form.rich_text_area` # rich text uses _rich_#{attribute}, but it is saved on #{attribute}, as well as error messages def attribute_or_rich_body case @input_type when :rich_text_area @attribute.to_s.sub(/\Arich_/, '').to_sym else @attribute end end def map_array_to_hash_with_true(array_or_string_or_nil) Array(array_or_string_or_nil).to_h { [_1, true] } end end