diff --git a/app/assets/stylesheets/attestation.scss b/app/assets/stylesheets/attestation.scss index e01a9168b..33cb38c48 100644 --- a/app/assets/stylesheets/attestation.scss +++ b/app/assets/stylesheets/attestation.scss @@ -178,4 +178,14 @@ bottom: 0; } } + + .tdc-repetition li { + margin-bottom: 5mm; + + dl { + display: grid; + grid-template-columns: auto 1fr; + gap: 1mm 5mm; + } + } } diff --git a/app/models/champ_presentations/repetition_presentation.rb b/app/models/champ_presentations/repetition_presentation.rb new file mode 100644 index 000000000..6a9c2bcb6 --- /dev/null +++ b/app/models/champ_presentations/repetition_presentation.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +class ChampPresentations::RepetitionPresentation + attr_reader :libelle + attr_reader :rows + + def initialize(libelle, rows) + @libelle = libelle + @rows = rows + end + + def to_s + ([libelle] + rows.map do |champs| + champs.map do |champ| + "#{champ.libelle} : #{champ}" + end.join("\n") + end).join("\n\n") + end + + def to_tiptap_node + { + type: 'orderedList', + attrs: { class: 'tdc-repetition' }, + content: rows.map do |champs| + { + type: 'listItem', + content: [ + { + type: 'descriptionList', + content: champs.map do |champ| + [ + { + type: 'descriptionTerm', + content: [ + { + type: 'text', + text: champ.libelle + } + ] + }, + { + type: 'descriptionDetails', + content: [ + { + type: 'text', + text: champ.to_s + } + ] + } + ] + end.flatten + } + ] + } + end + } + end +end diff --git a/app/models/champs/repetition_champ.rb b/app/models/champs/repetition_champ.rb index 1bae90913..7ea9d83cd 100644 --- a/app/models/champs/repetition_champ.rb +++ b/app/models/champs/repetition_champ.rb @@ -44,14 +44,6 @@ class Champs::RepetitionChamp < Champ # The user cannot enter any information here so it doesn’t make much sense to search end - def for_tag(path = :value) - ([libelle] + rows.map do |champs| - champs.map do |champ| - "#{champ.libelle} : #{champ}" - end.join("\n") - end).join("\n\n") - end - def rows_for_export row_ids.map.with_index(1) do |row_id, index| Champs::RepetitionChamp::Row.new(index:, row_id:, dossier:) diff --git a/app/models/types_de_champ/repetition_type_de_champ.rb b/app/models/types_de_champ/repetition_type_de_champ.rb index 492cfa0f2..740e572b1 100644 --- a/app/models/types_de_champ/repetition_type_de_champ.rb +++ b/app/models/types_de_champ/repetition_type_de_champ.rb @@ -1,6 +1,13 @@ # frozen_string_literal: true class TypesDeChamp::RepetitionTypeDeChamp < TypesDeChamp::TypeDeChampBase + def self.champ_value_for_tag(champ, path = :value) + return nil if path != :value + return champ_default_value if champ.rows.blank? + + ChampPresentations::RepetitionPresentation.new(champ.libelle, champ.rows) + end + def estimated_fill_duration(revision) estimated_rows_in_repetition = 2.5 diff --git a/app/services/tiptap_service.rb b/app/services/tiptap_service.rb index 6fd0a4e35..a55644353 100644 --- a/app/services/tiptap_service.rb +++ b/app/services/tiptap_service.rb @@ -79,10 +79,16 @@ class TiptapService "#{children(content, substitutions, level + 1)}" in type: 'bulletList', content: "" - in type: 'orderedList', content: - "
    #{children(content, substitutions, level + 1)}
" + in type: 'orderedList', content:, **rest + "#{children(content, substitutions, level + 1)}" in type: 'listItem', content: "
  • #{children(content, substitutions, level + 1)}
  • " + in type: 'descriptionList', content: + "
    #{children(content, substitutions, level + 1)}
    " + in type: 'descriptionTerm', content: + "
    #{children(content, substitutions, level + 1)}
    " + in type: 'descriptionDetails', content: + "
    #{children(content, substitutions, level + 1)}
    " in type: 'text', text:, **rest if rest[:marks].present? apply_marks(text, rest[:marks]) @@ -115,6 +121,12 @@ class TiptapService end end + def class_list(attrs) + if attrs.present? && attrs[:class].present? + " class=\"#{attrs[:class]}\"" + end + end + def apply_marks(text, marks) marks.reduce(text) do |text, mark| case mark diff --git a/spec/models/champ_presentations/repetition_presentation_spec.rb b/spec/models/champ_presentations/repetition_presentation_spec.rb new file mode 100644 index 000000000..de815b327 --- /dev/null +++ b/spec/models/champ_presentations/repetition_presentation_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +describe ChampPresentations::RepetitionPresentation do + let(:libelle) { "Langages de programmation" } + let(:procedure) { + create(:procedure, types_de_champ_public: [ + { + type: :repetition, + children: [ + { type: :text, libelle: "nom" }, + { type: :integer_number, libelle: "stars" } + ] + } + ]) + } + + let(:dossier) { create(:dossier, procedure:) } + + before do + nom, stars = dossier.champs[0].rows.first + nom.update(value: "ruby") + stars.update(value: 5) + + nom, stars = dossier.champs[0].add_row(dossier.procedure.active_revision) + nom.update(value: "rust") + stars.update(value: 4) + end + + let(:representation) { described_class.new(libelle, dossier.champs[0].reload.rows) } + + describe '#to_s' do + it 'returns a key-value representation' do + expect(representation.to_s).to eq( + <<~TXT.strip + Langages de programmation + + nom : ruby + stars : 5 + + nom : rust + stars : 4 + TXT + ) + end + end + + describe '#to_tiptap_node' do + it 'returns the correct HTML structure, without libelle' do + expected_node = { + type: "orderedList", + attrs: { class: "tdc-repetition" }, + content: [ + { + type: "listItem", + content: [ + { + type: "descriptionList", + content: [ + { content: [{ text: "nom", type: "text" }], type: "descriptionTerm" }, + { content: [{ text: "ruby", type: "text" }], type: "descriptionDetails" }, + { content: [{ text: "stars", type: "text" }], type: "descriptionTerm" }, + { content: [{ text: "5", type: "text" }], type: "descriptionDetails" } + ] + } + ] + }, { + type: "listItem", + content: [ + { + type: "descriptionList", + content: [ + { content: [{ text: "nom", type: "text" }], type: "descriptionTerm" }, + { content: [{ text: "rust", type: "text" }], type: "descriptionDetails" }, + { content: [{ text: "stars", type: "text" }], type: "descriptionTerm" }, + { content: [{ text: "4", type: "text" }], type: "descriptionDetails" } + ] + } + ] + } + ] + } + + expect(representation.to_tiptap_node).to eq(expected_node) + end + end +end diff --git a/spec/models/champs/repetition_champ_spec.rb b/spec/models/champs/repetition_champ_spec.rb new file mode 100644 index 000000000..d63dec474 --- /dev/null +++ b/spec/models/champs/repetition_champ_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +describe Champs::RepetitionChamp do + let(:procedure) { + create(:procedure, + types_de_champ_public: [ + { + type: :repetition, + children: [{ type: :text, libelle: "Ext" }], libelle: "Languages" + } + ]) + } + let(:dossier) { create(:dossier, procedure:) } + let(:champ) { dossier.champs.first } + + describe "#for_tag" do + before do + champ.rows[0][0].update(value: "rb") + end + + it "can render as string" do + expect(champ.for_tag.to_s).to eq( + <<~TXT.strip + Languages + + Ext : rb + TXT + ) + end + + it "as tiptap node" do + expect(champ.for_tag.to_tiptap_node).to include(type: 'orderedList') + end + end +end diff --git a/spec/services/tiptap_service_spec.rb b/spec/services/tiptap_service_spec.rb index 669a70329..287e76985 100644 --- a/spec/services/tiptap_service_spec.rb +++ b/spec/services/tiptap_service_spec.rb @@ -201,6 +201,40 @@ RSpec.describe TiptapService do expect(described_class.new.to_html(json, substitutions)).to eq("

    The Title

    First paragraph

    ") end end + + context 'ordered list with custom classes' do + let(:json) do + { + type: 'doc', + content: [ + { + type: 'orderedList', + attrs: { class: "my-class" }, + content: [ + { + type: 'listItem', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'Item 1' + } + ] + } + ] + } + ] + } + ] + } + end + + it "set class attribute" do + expect(described_class.new.to_html(json, substitutions)).to eq('
    1. Item 1

    ') + end + end end describe '#used_tags' do