diff --git a/.scss-lint.yml b/.scss-lint.yml index dfa4bc00d..d615d0ea1 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -150,7 +150,7 @@ linters: properties: {} PseudoElement: - enabled: true + enabled: false # otherwise rules on ::marker fails # To enable later QualifyingElement: diff --git a/app/assets/stylesheets/dsfr.scss b/app/assets/stylesheets/dsfr.scss index ef709bd1e..a2f734412 100644 --- a/app/assets/stylesheets/dsfr.scss +++ b/app/assets/stylesheets/dsfr.scss @@ -1,5 +1,15 @@ @import "colors"; +// overwrite DSFR style for SimpleFormatComponent, some user use markdown with +// ordered list having paragraph between list item +.fr-ol-content--override { + list-style-type: decimal; + + li::marker { + content: inherit; + } +} + // override default transparent background on inputs & font-size to 16px by default input, textarea, diff --git a/app/components/simple_format_component.rb b/app/components/simple_format_component.rb index ac3a061c6..84db5d841 100644 --- a/app/components/simple_format_component.rb +++ b/app/components/simple_format_component.rb @@ -1,14 +1,14 @@ class SimpleFormatComponent < ApplicationComponent # see: https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use REDCARPET_EXTENSIONS = { - no_intra_emphasis: false, + no_intra_emphasis: true, + disable_indented_code_blocks: true, + space_after_headers: true, tables: false, fenced_code_blocks: false, autolink: false, - disable_indented_code_blocks: false, strikethrough: false, lax_spacing: false, - space_after_headers: false, superscript: false, underline: false, highlight: false, @@ -27,18 +27,31 @@ class SimpleFormatComponent < ApplicationComponent def initialize(text, allow_a: true, class_names_map: {}) @allow_a = allow_a - @text = (text || "").gsub(/\R/, "\n\n") # force double \n otherwise a single one won't split paragraph - .split("\n\n") + list_item = false + lines = (text || "") + .lines .map(&:lstrip) # this block prevent redcarpet to consider " text" as block code by lstriping - .join("\n\n") - .gsub(EMAIL_IN_TEXT_REGEX) { _1.gsub('_', '\\_') } # Workaround for redcarpet bug on autolink email having _. Cf tests - if !@allow_a - @text = @text.gsub(SIMPLE_URL_REGEX) { _1.gsub('_', '\\_') } # Escape underscores in URLs - end + @text = lines.map do |line| + item_number = line.match(/\A(\d+)\./) + if item_number.present? + list_item = true + "\n" + line + "[value:#{item_number[1]}]" + elsif line.match?(/\A[-*+]\s/) + list_item = true + "\n" + line + elsif line == '' + list_item = false + "\n" + line + elsif list_item + line + else + "\n" + line + end + end.join.lstrip @renderer = Redcarpet::Markdown.new( - Redcarpet::BareRenderer.new(class_names_map:), + Redcarpet::BareRenderer.new(class_names_map: { list: 'fr-ol-content--override' }), REDCARPET_EXTENSIONS.merge(autolink: @allow_a) ) end @@ -52,6 +65,6 @@ class SimpleFormatComponent < ApplicationComponent end def attributes - ['target', 'rel', 'href', 'class', 'title'] + ['target', 'rel', 'href', 'class', 'title', 'value'] end end diff --git a/app/lib/redcarpet/bare_renderer.rb b/app/lib/redcarpet/bare_renderer.rb index 5e4e9850a..d8944374c 100644 --- a/app/lib/redcarpet/bare_renderer.rb +++ b/app/lib/redcarpet/bare_renderer.rb @@ -10,7 +10,14 @@ module Redcarpet end def list_item(content, list_type) - content_tag(:li, content.strip.gsub(/<\/?p>/, ''), {}, false) + item_number = content.match(/\[value:(\d+)\]/) + text = content.strip + .gsub(/<\/?p>/, '') + .gsub(/\[value:\d+\]/, '') + .gsub(/\n/, '
') + attributes = item_number.present? ? { value: item_number[1] } : {} + + content_tag(:li, text, attributes, false) end def paragraph(text) diff --git a/spec/components/simple_format_component_spec.rb b/spec/components/simple_format_component_spec.rb index dc6cf6cbb..c0c9be838 100644 --- a/spec/components/simple_format_component_spec.rb +++ b/spec/components/simple_format_component_spec.rb @@ -47,11 +47,59 @@ TEXT <<~TEXT 1. 1er paragraphe 2. paragraphe + 4. 4eme paragraphe TEXT end it { expect(page).to have_selector("ol", count: 1) } - it { expect(page).to have_selector("li", count: 2) } + it { expect(page).to have_selector("li", count: 3) } + it { expect(page.native.inner_html).to match('value="1"') } + it { expect(page.native.inner_html).to match('value="4"') } + end + + context 'multi line lists' do + let(:text) do + <<~TEXT + Lorsque nous souhaitons envoyer ce message : + + 1. Premier point de la recette + Commentaire 1 + 2. Deuxième point de la recette + Commentaire 2 + + 4. Troisième point de la recette + Commentaire 3 + + trois nouveaux paragraphes + sur plusieures + lignes + + - 1er point de la recette + * 2eme point de la recette + avec des détailles + + 3eme point de la recette + beaucoup + de détails + + conclusion + TEXT + end + + it { expect(page).to have_selector("ol", count: 1) } + it { expect(page).to have_selector("ul", count: 1) } + it { expect(page).to have_selector("li", count: 6) } + it { expect(page).to have_selector("p", count: 5) } + end + + context 'strong' do + let(:text) do + <<~TEXT + 1er paragraphe **fort** un_mot_pas_italic + TEXT + end + + it { expect(page).to have_selector("strong", count: 1) } + it { expect(page).not_to have_selector("em") } end context 'auto-link' do