tech(remaniement): isole la logique de rendu au champs_subtree_component

This commit is contained in:
Martin 2023-04-12 17:55:08 +02:00 committed by mfo
parent 3b5d1bd55b
commit 88abefb370
10 changed files with 137 additions and 191 deletions

View file

@ -1,41 +1,47 @@
class EditableChamp::ChampsSubtreeComponent < ApplicationComponent
include ApplicationHelper
include TreeableConcern
attr_reader :header_section, :nodes
def initialize(header_section:)
@header_section = header_section
@nodes = []
end
# a nodes can be either a champs, or a subtree
def add_node(node)
nodes.push(node)
def initialize(nodes:)
@nodes = to_fieldset(nodes:)
end
def render_within_fieldset?
header_section && !empty_section?
first_champ_is_an_header_section? && any_champ_fillable?
end
def render_header_section_only?
header_section && empty_section?
def header_section
first_champ = @nodes.first
return first_champ if first_champ.is_a?(Champs::HeaderSectionChamp)
nil
end
def empty_section?
nodes.none? { |node| node.is_a?(Champ) }
end
def champs
return @nodes if !first_champ_is_an_header_section?
_, *rest_of_champ = @nodes
def level
if header_section.parent.present?
header_section.header_section_level_value.to_i + header_section.parent.current_section_level
elsif header_section
header_section.header_section_level_value.to_i
else
0
end
rest_of_champ
end
def tag_for_depth
"h#{level + 1}"
"h#{header_section.level + 1}"
end
def fillable?
false
end
private
def to_fieldset(nodes:)
nodes.map { _1.is_a?(Array) ? EditableChamp::ChampsSubtreeComponent.new(nodes: _1) : _1 }
end
def first_champ_is_an_header_section?
header_section.present?
end
def any_champ_fillable?
champs.any? { _1&.fillable? }
end
end

View file

@ -1,17 +1,17 @@
- if render_within_fieldset?
= tag.fieldset(class: "reset-#{tag_for_depth}") do
= tag.legend do
= render EditableChamp::HeaderSectionComponent.new(champ: header_section, form: nil, level: level)
- @nodes.each do |champ_or_section|
- if champ_or_section.is_a?(Champ)
= render EditableChamp::HeaderSectionComponent.new(champ: header_section, form: nil)
- champs.each do |champ_or_section|
- if !champ_or_section.is_a?(EditableChamp::ChampsSubtreeComponent)
= render EditableChamp::FieldsForChampComponent.new(champ: champ_or_section, seen_at: nil)
- else
= render champ_or_section
- elsif render_header_section_only?
= render EditableChamp::HeaderSectionComponent.new(champ: header_section, form: nil, level: level)
- else
- @nodes.each do |champ_or_section|
- if champ_or_section.is_a?(Champ)
- if header_section
= render EditableChamp::HeaderSectionComponent.new(champ: header_section, form: nil)
- champs.each do |champ_or_section|
- if !champ_or_section.is_a?(EditableChamp::ChampsSubtreeComponent)
= render EditableChamp::FieldsForChampComponent.new(champ: champ_or_section, seen_at: nil)
- else
= render champ_or_section

View file

@ -1,13 +1,7 @@
class EditableChamp::ChampsTreeComponent < ApplicationComponent
include Champs::Treeable
attr_reader :root
include TreeableConcern
def initialize(champs:, root_depth:)
@root = to_tree(champs:, root_depth:, build_champs_subtree_component: method(:build_champs_subtree_component))
end
def build_champs_subtree_component(header_section:)
EditableChamp::ChampsSubtreeComponent.new(header_section:)
@tree = to_tree(champs:, root_depth:)
end
end

View file

@ -1 +1 @@
= render @root
= render EditableChamp::ChampsSubtreeComponent.new(nodes: @tree)

View file

@ -1,8 +1,11 @@
class EditableChamp::HeaderSectionComponent < ApplicationComponent
def initialize(form:, champ:, seen_at: nil, level: 1)
def initialize(form:, champ:, seen_at: nil)
@champ = champ
@form = form
@level = level
end
def level
@champ.level
end
def libelle
@ -10,13 +13,13 @@ class EditableChamp::HeaderSectionComponent < ApplicationComponent
end
def header_section_classnames
class_names = ["fr-h#{@level}", 'header-section']
class_names = ["fr-h#{level}", 'header-section']
class_names << 'header-section-counter' if @champ.dossier.auto_numbering_section_headers_for?(@champ)
class_names
end
def tag_for_depth
"h#{@level + 1}"
"h#{level + 1}"
end
end

View file

@ -21,7 +21,6 @@
# type_de_champ_id :integer
#
class Champs::HeaderSectionChamp < Champ
def level
if parent.present?
header_section_level_value.to_i + parent.current_section_level

View file

@ -420,7 +420,6 @@ class TypeDeChamp < ApplicationRecord
end
end
def previous_section_level(upper_tdcs)
previous_header_section = upper_tdcs.reverse.find(&:header_section?)

View file

@ -0,0 +1,86 @@
describe EditableChamp::ChampsSubtreeComponent, type: :component do
include TreeableConcern
let(:component) { described_class.new(nodes: nodes) }
let(:nodes) { to_tree(champs:, root_depth:) }
let(:root_depth) { 0 }
before { render_inline(component).to_html }
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")
end
it 'renders champs' do
expect(page).to have_selector("input[type=text]", count: 1)
expect(page).to have_selector("textarea", count: 1)
end
end
context 'list of champs with an header_section' do
let(:champs) { [build(:champ_header_section_level_1), build(:champ_text), build(:champ_textarea)] }
it 'renders fieldset' do
expect(page).to have_selector("fieldset")
expect(page).to have_selector("legend h2")
end
it 'renders champs within fieldset' do
expect(page).to have_selector("fieldset input[type=text]")
expect(page).to have_selector("fieldset textarea")
end
end
context 'list of champs without section and an header_section having champs' 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("legend h2")
end
it 'renders all champs, one outside fieldset, one within fieldset' do
expect(page).to have_selector("input[type=text]", count: 2)
expect(page).to have_selector("fieldset input[type=text]", count: 1)
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")
expect(page).to have_selector("h2")
expect(page).to have_selector("h3")
expect(page).to have_selector("h4")
end
end
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
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 input[type=text]", count: 1)
end
end
context 'nested fieldsset' do
let(:champs) { [build(:champ_header_section_level_1), build(:champ_text), build(:champ_header_section_level_2), build(:champ_textarea)] }
it 'render nested fieldsets' do
expect(page).to have_selector("fieldset")
expect(page).to have_selector("legend h2")
expect(page).to have_selector("fieldset fieldset")
expect(page).to have_selector("fieldset fieldset legend h3")
end
it 'contains all champs' do
expect(page).to have_selector("fieldset input[type=text]", count: 1)
expect(page).to have_selector("fieldset fieldset textarea", count: 1)
end
end
end

View file

@ -1,142 +0,0 @@
describe EditableChamp::ChampsTreeComponent, type: :component do
let(:component) { described_class.new(champs: champs, root_depth: 0) }
subject { component.root }
describe "to_tree" do
let(:header_1) { build(:champ_header_section_level_1) }
let(:header_1_2) { build(:champ_header_section_level_2) }
let(:header_2) { build(:champ_header_section_level_1) }
let(:champ_text) { build(:champ_text) }
let(:champ_textarea) { build(:champ_textarea) }
let(:champ_explication) { build(:champ_explication) }
let(:champ_communes) { build(:champ_communes) }
context 'without section' do
let(:champs) do
[
champ_text, champ_textarea
]
end
it 'chunk by uniq champs' do
expect(subject.header_section).to eq(nil)
expect(subject.nodes.size).to eq(champs.size)
expect(subject.nodes).to eq(champs)
end
end
context 'with sections only' do
let(:champs) do
[
header_1,
champ_explication,
champ_text,
header_2,
champ_textarea
]
end
it 'chunk by uniq champs' do
expect(subject.nodes.size).to eq(2)
expect(subject.nodes[0].header_section).to eq(header_1)
expect(subject.nodes[0].nodes).to eq([champ_explication, champ_text])
expect(subject.nodes[1].header_section).to eq(header_2)
expect(subject.nodes[1].nodes).to eq([champ_textarea])
end
end
context 'leading champs, and in between sections only' do
let(:champs) do
[
champ_text,
champ_textarea,
header_1,
champ_explication,
champ_communes,
header_2,
champ_textarea
]
end
it 'chunk by uniq champs' do
expect(subject.nodes.size).to eq(4)
expect(subject.nodes[0]).to eq(champ_text)
expect(subject.nodes[1]).to eq(champ_textarea)
expect(subject.nodes[2].header_section).to eq(header_1)
expect(subject.nodes[2].nodes).to eq([champ_explication, champ_communes])
expect(subject.nodes[3].header_section).to eq(header_2)
expect(subject.nodes[3].nodes).to eq([champ_textarea])
end
end
context 'with one sub sections' do
let(:champs) do
[
header_1,
champ_explication,
header_1_2,
champ_communes,
header_2,
champ_textarea
]
end
it 'chunk by uniq champs' do
expect(subject.nodes.size).to eq(2)
expect(subject.nodes[0].header_section).to eq(header_1)
expect(subject.nodes[0].nodes.size).to eq(2)
expect(subject.nodes[0].nodes[1].header_section).to eq(header_1_2)
expect(subject.nodes[0].nodes[1].nodes).to eq([champ_communes])
expect(subject.nodes[1].header_section).to eq(header_2)
expect(subject.nodes[1].nodes).to eq([champ_textarea])
end
end
context 'with consecutive subsection' do
let(:header_1) { build(:champ_header_section_level_1) }
let(:header_1_2_1) { build(:champ_header_section_level_2) }
let(:header_1_2_2) { build(:champ_header_section_level_2) }
let(:header_1_2_3) { build(:champ_header_section_level_2) }
let(:champs) do
[
header_1,
header_1_2_1,
build(:champ_text),
header_1_2_2,
build(:champ_text),
header_1_2_3,
build(:champ_text)
]
end
it 'chunk by uniq champs' do
expect(subject.nodes.size).to eq(1)
end
end
context 'with one sub sections and one subsub section' do
let(:header_1_2_3) { build(:champ_header_section_level_3) }
let(:champs) do
[
header_1,
champ_explication,
header_1_2,
champ_communes,
header_1_2_3,
champ_text,
header_2,
champ_textarea
]
end
it 'chunk by uniq champs' do
expect(subject.nodes.size).to eq(2)
expect(subject.nodes[0].header_section).to eq(header_1)
expect(subject.nodes[0].nodes.size).to eq(2)
expect(subject.nodes[0].nodes[1].header_section).to eq(header_1_2)
expect(subject.nodes[0].nodes[1].nodes.size).to eq(2)
expect(subject.nodes[0].nodes[1].nodes.first).to eq(champ_communes)
expect(subject.nodes[0].nodes[1].nodes[1].header_section).to eq(header_1_2_3)
expect(subject.nodes[0].nodes[1].nodes[1].nodes).to eq([champ_text])
expect(subject.nodes[1].header_section).to eq(header_2)
expect(subject.nodes[1].nodes).to eq([champ_textarea])
end
end
end
end

View file

@ -1,6 +1,7 @@
describe TreeableConcern do
class ChampsToTree
include TreeableConcern
attr_reader :root
def initialize(champs:, root_depth:)
@root = to_tree(champs:, root_depth:)
@ -43,8 +44,8 @@ describe TreeableConcern do
it 'wraps champs within preview header section' do
expect(subject.size).to eq(2)
expect(subject).to eq([
[ header_1, champ_explication, champ_text ],
[ header_2, champ_textarea ]
[header_1, champ_explication, champ_text],
[header_2, champ_textarea]
])
end
end