diff --git a/app/assets/stylesheets/attestation_template_2_edit.scss b/app/assets/stylesheets/attestation_template_2_edit.scss index 11b74ee2c..b726e9a8d 100644 --- a/app/assets/stylesheets/attestation_template_2_edit.scss +++ b/app/assets/stylesheets/attestation_template_2_edit.scss @@ -75,4 +75,14 @@ background-color: var(--background-action-low-blue-france); } } + + // scss-lint:disable SelectorFormat + #show_maybe_null + label { + margin-bottom: 0.25rem; + + .fr-hint-text { + position: absolute; + top: 1.15rem; + } + } } diff --git a/app/components/tags_button_list_component.rb b/app/components/tags_button_list_component.rb index ca5798625..1f5b6f03b 100644 --- a/app/components/tags_button_list_component.rb +++ b/app/components/tags_button_list_component.rb @@ -14,4 +14,18 @@ class TagsButtonListComponent < ApplicationComponent def button_title(tag) tag[:description].presence || tag[:libelle] end + + def each_category + tags.each_pair do |category, tags| + yield category, tags, can_toggle_nullable?(category) + end + end + + private + + def can_toggle_nullable?(category) + return false if category != :champ_public + + tags[category].any? { _1[:maybe_null] } + end end diff --git a/app/components/tags_button_list_component/tags_button_list_component.html.haml b/app/components/tags_button_list_component/tags_button_list_component.html.haml index fa03ff4af..74f66a55d 100644 --- a/app/components/tags_button_list_component/tags_button_list_component.html.haml +++ b/app/components/tags_button_list_component/tags_button_list_component.html.haml @@ -1,8 +1,18 @@ -- tags.each_pair do |category, tags| - %p.fr-label.fr-text--sm.fr-text--bold.fr-mb-1w= t(category, scope: ".categories") - %ul.fr-tags-group +- each_category do |category, tags, can_toggle_nullable| + .flex + %p.fr-label.fr-text--sm.fr-text--bold.fr-mb-1w= t(category, scope: ".categories") + + - if can_toggle_nullable + .fr-fieldset__element.fr-ml-4w + .fr-checkbox-group.fr-checkbox-group--sm + = check_box_tag("show_maybe_null", 1, false, data: { "no-autosubmit" => true, action: "change->attestation#toggleMaybeNull"}) + = label_tag "show_maybe_null", for: :show_maybe_null do + Voir les champs facultatifs + %span.hidden.fr-hint-text Un champ non rempli restera vide dans l’attestation. + + %ul.fr-tags-group{ data: { category: category } } - tags.each do |tag| - %li + %li{ class: class_names("hidden" => can_toggle_nullable && tag[:maybe_null]), data: { "maybe-null" => can_toggle_nullable && tag[:maybe_null].present? } } - label = button_label(tag) %button.fr-tag.fr-tag--sm{ type: "button", title: button_title(tag), data: { action: 'click->tiptap#insertTag', tiptap_target: 'tag', tag_id: tag[:id], tag_label: label } } = label diff --git a/app/javascript/controllers/attestation_controller.ts b/app/javascript/controllers/attestation_controller.ts index 74ba04a05..cf9b45d91 100644 --- a/app/javascript/controllers/attestation_controller.ts +++ b/app/javascript/controllers/attestation_controller.ts @@ -32,6 +32,20 @@ export class AttestationController extends ApplicationController { }); } + toggleMaybeNull(event: Event) { + const checkbox = event.target as HTMLInputElement; + const visible = checkbox.checked; + + // toggle hidden class on next label element + checkbox.nextElementSibling + ?.querySelector('.fr-hint-text') + ?.classList?.toggle('hidden', !visible); + + document.querySelectorAll('li[data-maybe-null]').forEach((tag) => { + tag.classList.toggle('hidden', !visible); + }); + } + private get isStateLayout() { return this.layoutToggleTarget.checked; } diff --git a/app/models/types_de_champ/type_de_champ_base.rb b/app/models/types_de_champ/type_de_champ_base.rb index 9deb49a3d..7efc30f85 100644 --- a/app/models/types_de_champ/type_de_champ_base.rb +++ b/app/models/types_de_champ/type_de_champ_base.rb @@ -1,7 +1,7 @@ class TypesDeChamp::TypeDeChampBase include ActiveModel::Validations - delegate :description, :libelle, :mandatory, :stable_id, :fillable?, to: :@type_de_champ + delegate :description, :libelle, :mandatory, :mandatory?, :stable_id, :fillable?, :public?, to: :@type_de_champ FILL_DURATION_SHORT = 10.seconds FILL_DURATION_MEDIUM = 1.minute @@ -19,6 +19,7 @@ class TypesDeChamp::TypeDeChampBase libelle: TagsSubstitutionConcern::TagsParser.normalize(libelle), id: "tdc#{stable_id}", description: description, + maybe_null: public? && !mandatory?, lambda: -> (champs) { champs.find { |champ| champ.stable_id == stable_id }&.for_tag } diff --git a/spec/components/previews/tags_button_list_component_preview.rb b/spec/components/previews/tags_button_list_component_preview.rb index 2e46fdeb3..23a7ad49f 100644 --- a/spec/components/previews/tags_button_list_component_preview.rb +++ b/spec/components/previews/tags_button_list_component_preview.rb @@ -18,7 +18,8 @@ class TagsButtonListComponentPreview < ViewComponent::Preview { id: 'tdc13', libelle: 'Votre avis très ' + 'long ' * 12, - description: 'Ce libellé a été tronqué' + description: 'Ce libellé a été tronqué', + maybe_null: true } ], diff --git a/spec/components/tags_button_list_component_spec.rb b/spec/components/tags_button_list_component_spec.rb new file mode 100644 index 000000000..226c64090 --- /dev/null +++ b/spec/components/tags_button_list_component_spec.rb @@ -0,0 +1,56 @@ +RSpec.describe TagsButtonListComponent, type: :component do + let(:tags) do + { + individual: TagsSubstitutionConcern::INDIVIDUAL_TAGS, + etablissement: TagsSubstitutionConcern::ENTREPRISE_TAGS, + dossier: TagsSubstitutionConcern::DOSSIER_TAGS, + champ_public: [ + { + id: 'tdc12', + libelle: 'Votre avis', + description: 'Détaillez votre avis' + }, + { + id: 'tdc13', + libelle: 'Un champ avec un nom très ' + 'long ' * 12, + description: 'Ce libellé a été tronqué', + maybe_null: + } + ], + + champ_private: [ + { + id: 'tdc22', + libelle: 'Montant accordé' + } + ] + } + end + let(:maybe_null) { true } + + let(:component) do + described_class.new(tags:) + end + + subject { render_inline(component).to_html } + + it 'renders' do + expect(subject).to have_text("Identité") + expect(subject).to have_text("civilité") + expect(subject).to have_text("Votre avis") + expect(subject).to have_text("Montant accordé") + end + + it "hide nullable tag" do + expect(subject).to have_selector(".hidden button.fr-tag", text: "Un champ avec un nom") + expect(subject).to have_selector(":not(.hidden) button.fr-tag", text: "Votre avis") + expect(subject).to have_text("Voir les champs facultatifs") + end + + context "all champs are visible" do + let(:maybe_null) { false } + it { + expect(subject).not_to have_text("Voir les champs facultatifs") + } + end +end