chore(form): respect dsfr fr-fieldset and elements expected markup

This commit is contained in:
Colin Darie 2023-09-27 18:43:45 +02:00
parent 2cec44109d
commit 60882e0844
No known key found for this signature in database
GPG key ID: 8C76CADD40253590
4 changed files with 88 additions and 36 deletions

View file

@ -27,7 +27,7 @@
.section-2 { .section-2 {
margin-top: 1.5rem; margin-top: 1.5rem;
padding-top: 1rem; padding-top: 2rem;
border-top: 2px solid var(--border-default-grey); border-top: 2px solid var(--border-default-grey);
} }
@ -44,6 +44,11 @@
margin-bottom: 1rem; 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 { legend {
width: 100%; width: 100%;
} }

View file

@ -8,11 +8,11 @@ class EditableChamp::SectionComponent < ApplicationComponent
end end
def render_within_fieldset? def render_within_fieldset?
first_champ_is_an_header_section? && any_champ_fillable? first_champ_is_an_header_section?
end end
def header_section def header_section
return @nodes.first if @nodes.first.is_a?(Champs::HeaderSectionChamp) @nodes.first if @nodes.first.is_a?(Champs::HeaderSectionChamp)
end end
def splitted_tail def splitted_tail
@ -30,13 +30,6 @@ class EditableChamp::SectionComponent < ApplicationComponent
"h#{header_section.level + 1}" "h#{header_section.level + 1}"
end 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) def split_section_champ(node)
case node case node
when EditableChamp::SectionComponent when EditableChamp::SectionComponent
@ -55,8 +48,4 @@ class EditableChamp::SectionComponent < ApplicationComponent
def first_champ_is_an_header_section? def first_champ_is_an_header_section?
header_section.present? header_section.present?
end end
def any_champ_fillable?
tail.any? { _1&.fillable? }
end
end end

View file

@ -1,19 +1,24 @@
- if render_within_fieldset? - if render_within_fieldset?
= tag.fieldset(class: "reset-#{tag_for_depth} fr-m-0 fr-p-0") do .fr-fieldset__element{ class: class_names("fr-my-0" => render_within_fieldset?) }
= tag.legend do %fieldset.fr-fieldset.fr-my-0{ class: "reset-#{tag_for_depth}" }
%legend.fr-fieldset__legend
= render EditableChamp::HeaderSectionComponent.new(champ: header_section) = render EditableChamp::HeaderSectionComponent.new(champ: header_section)
- splitted_tail.each do |section, champ| - splitted_tail.each do |section, champ|
- if section.present? - if section.present?
= render section = render section
- else - else
= fields_for champ.input_name, champ do |form| = fields_for champ.input_name, champ do |form|
= render EditableChamp::EditableChampComponent.new form: ,champ: .fr-fieldset__element
= render EditableChamp::EditableChampComponent.new form:, champ:
- else - else
%fieldset.fr-fieldset.fr-my-0
- if header_section - if header_section
%div{ class: "reset-#{tag_for_depth}" }= render EditableChamp::HeaderSectionComponent.new(champ: 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| - splitted_tail.each do |section, champ|
- if section.present? - if section.present?
= render section = render section
- else - else
= fields_for champ.input_name, champ do |form| = fields_for champ.input_name, champ do |form|
= render EditableChamp::EditableChampComponent.new form: ,champ: .fr-fieldset__element
= render EditableChamp::EditableChampComponent.new form:, champ:

View file

@ -6,8 +6,8 @@ describe EditableChamp::SectionComponent, type: :component do
context 'list of champs without an header_section' do context 'list of champs without an header_section' do
let(:champs) { [build(:champ_text), build(:champ_textarea)] } let(:champs) { [build(:champ_text), build(:champ_textarea)] }
it 'does not render fieldset' do it 'render in a fieldset' do
expect(page).not_to have_selector("fieldset") expect(page).to have_selector("fieldset", count: 1)
end end
it 'renders champs' do 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)] } let(:champs) { [build(:champ_text), build(:champ_header_section_level_1), build(:champ_text)] }
it 'renders fieldset' do 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") expect(page).to have_selector("legend h2")
end 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("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
end end
context 'list of header_section without champs' do 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)] } 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 it 'render header within fieldset' do
expect(page).not_to have_selector("fieldset") expect(page).to have_selector("fieldset > legend", count: 3)
expect(page).to have_selector("h2") expect(page).to have_selector("h2")
expect(page).to have_selector("h3") expect(page).to have_selector("h3")
expect(page).to have_selector("h4") 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 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)] } 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("h2", count: 2)
expect(page).to have_selector("h3") # explication 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) expect(page).to have_selector("fieldset input[type=text]", count: 1)
end end
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) expect(page).to have_selector("fieldset fieldset input[type=text]", count: dossier.champs_public.find(&:repetition?).rows.size)
end end
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 end