diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index 0d85c92ca..9c1f9b312 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -27,7 +27,7 @@ .section-2 { margin-top: 1.5rem; - padding-top: 1rem; + padding-top: 2rem; border-top: 2px solid var(--border-default-grey); } @@ -44,6 +44,11 @@ margin-bottom: 1rem; } + // Keep only bottom margin in nested (consecutive) header sections, ie. first legend for a same level + .fr-fieldset > .fr-fieldset__legend + .fr-fieldset__element > .fr-fieldset:first-of-type .header-section { + margin-top: 0 !important; + } + legend { width: 100%; } diff --git a/app/components/editable_champ/section_component.rb b/app/components/editable_champ/section_component.rb index 12a14f01d..55007bc66 100644 --- a/app/components/editable_champ/section_component.rb +++ b/app/components/editable_champ/section_component.rb @@ -8,11 +8,11 @@ class EditableChamp::SectionComponent < ApplicationComponent end def render_within_fieldset? - first_champ_is_an_header_section? && any_champ_fillable? + first_champ_is_an_header_section? end def header_section - return @nodes.first if @nodes.first.is_a?(Champs::HeaderSectionChamp) + @nodes.first if @nodes.first.is_a?(Champs::HeaderSectionChamp) end def splitted_tail @@ -30,13 +30,6 @@ class EditableChamp::SectionComponent < ApplicationComponent "h#{header_section.level + 1}" end - # if two headers follows each others [h1, [h2, c]] - # the first one must not be contained in fieldset - # so we make the tree not fillable - def fillable? - false - end - def split_section_champ(node) case node when EditableChamp::SectionComponent @@ -55,8 +48,4 @@ class EditableChamp::SectionComponent < ApplicationComponent def first_champ_is_an_header_section? header_section.present? end - - def any_champ_fillable? - tail.any? { _1&.fillable? } - end end diff --git a/app/components/editable_champ/section_component/section_component.html.haml b/app/components/editable_champ/section_component/section_component.html.haml index 8844a551c..e1c5d0550 100644 --- a/app/components/editable_champ/section_component/section_component.html.haml +++ b/app/components/editable_champ/section_component/section_component.html.haml @@ -1,19 +1,24 @@ - if render_within_fieldset? - = tag.fieldset(class: "reset-#{tag_for_depth} fr-m-0 fr-p-0") do - = tag.legend do - = render EditableChamp::HeaderSectionComponent.new(champ: header_section) + .fr-fieldset__element{ class: class_names("fr-my-0" => render_within_fieldset?) } + %fieldset.fr-fieldset.fr-my-0{ class: "reset-#{tag_for_depth}" } + %legend.fr-fieldset__legend + = render EditableChamp::HeaderSectionComponent.new(champ: header_section) + - splitted_tail.each do |section, champ| + - if section.present? + = render section + - else + = fields_for champ.input_name, champ do |form| + .fr-fieldset__element + = render EditableChamp::EditableChampComponent.new form:, champ: +- else + %fieldset.fr-fieldset.fr-my-0 + - if header_section + %legend.fr-fieldset__legend.fr-my-0{ class: "reset-#{tag_for_depth}" } + = render EditableChamp::HeaderSectionComponent.new(champ: header_section) - splitted_tail.each do |section, champ| - if section.present? = render section - else = fields_for champ.input_name, champ do |form| - = render EditableChamp::EditableChampComponent.new form: ,champ: -- else - - if header_section - %div{ class: "reset-#{tag_for_depth}" }= render EditableChamp::HeaderSectionComponent.new(champ: header_section) - - splitted_tail.each do |section, champ| - - if section.present? - = render section - - else - = fields_for champ.input_name, champ do |form| - = render EditableChamp::EditableChampComponent.new form: ,champ: + .fr-fieldset__element + = render EditableChamp::EditableChampComponent.new form:, champ: diff --git a/spec/components/editable_champ/section_component_spec.rb b/spec/components/editable_champ/section_component_spec.rb index 511fa7898..da5283530 100644 --- a/spec/components/editable_champ/section_component_spec.rb +++ b/spec/components/editable_champ/section_component_spec.rb @@ -6,8 +6,8 @@ describe EditableChamp::SectionComponent, type: :component do context 'list of champs without an header_section' do let(:champs) { [build(:champ_text), build(:champ_textarea)] } - it 'does not render fieldset' do - expect(page).not_to have_selector("fieldset") + it 'render in a fieldset' do + expect(page).to have_selector("fieldset", count: 1) end it 'renders champs' do @@ -34,21 +34,21 @@ describe EditableChamp::SectionComponent, type: :component do let(:champs) { [build(:champ_text), build(:champ_header_section_level_1), build(:champ_text)] } it 'renders fieldset' do - expect(page).to have_selector("fieldset") + expect(page).to have_selector("fieldset", count: 2) expect(page).to have_selector("legend h2") end - it 'renders all champs, one outside fieldset, one within fieldset' do + it 'renders all champs, each in its fieldset' do expect(page).to have_selector("input[type=text]", count: 2) - expect(page).to have_selector("fieldset input[type=text]", count: 1) + expect(page).to have_selector("fieldset > .fr-fieldset__element input[type=text]", count: 2) end end context 'list of header_section without champs' do let(:champs) { [build(:champ_header_section_level_1), build(:champ_header_section_level_2), build(:champ_header_section_level_3)] } - it 'does not render header within fieldset' do - expect(page).not_to have_selector("fieldset") + it 'render header within fieldset' do + expect(page).to have_selector("fieldset > legend", count: 3) expect(page).to have_selector("h2") expect(page).to have_selector("h3") expect(page).to have_selector("h4") @@ -58,10 +58,10 @@ describe EditableChamp::SectionComponent, type: :component do context 'header_section followed by explication and another fieldset' do let(:champs) { [build(:champ_header_section_level_1), build(:champ_explication), build(:champ_header_section_level_1), build(:champ_text)] } - it 'render fieldset, header_section (one within fieldset, one outside), also render explication' do + it 'render fieldset, header_section, also render explication' do expect(page).to have_selector("h2", count: 2) expect(page).to have_selector("h3") # explication - expect(page).to have_selector("fieldset h2", count: 1) + expect(page).to have_selector("fieldset > legend > h2", count: 2) expect(page).to have_selector("fieldset input[type=text]", count: 1) end end @@ -110,4 +110,57 @@ describe EditableChamp::SectionComponent, type: :component do expect(page).to have_selector("fieldset fieldset input[type=text]", count: dossier.champs_public.find(&:repetition?).rows.size) end end + + context 'with complex markup structure' do + def check_fieldset_structure(fieldset) + expect(fieldset[:class]).to include('fr-fieldset') + + # Vérifie que chaque fr-fieldset a un enfant fr-fieldset__element ou une légende + fieldset.all('> *').each do |child| + expect(child.tag_name).to be_in(['div', 'legend', 'input']) + + case child.tag_name + when 'legend' + expect(child[:class]).to include('fr-fieldset__legend') + when 'input' + expect(child[:type]).to eq("hidden") + else + expect(child[:class]).to include('fr-fieldset__element') + # Vérifie récursivement les fieldsets imbriqués + child.all('> fieldset').each do |nested_fieldset| + check_fieldset_structure(nested_fieldset) + end + end + end + end + + let(:champs) { + [ + build(:champ_header_section_level_1), + build(:champ_header_section_level_2), + build(:champ_header_section_level_3), + build(:champ_integer_number), + + build(:champ_header_section_level_3), + build(:champ_yes_no), + + build(:champ_header_section_level_2), + build(:champ_header_section_level_3), + build(:champ_integer_number), + + build(:champ_header_section_level_1), + build(:champ_text), + build(:champ_header_section_level_2), + build(:champ_text) + ] + } + + it 'respect dsfr fieldset hierarchy' do + within('.dossier-edit .form') do + all('fieldset').each do |fieldset| + check_fieldset_structure(fieldset) + end + end + end + end end