fix(markdown): render ordered lists with custom values and handle multiline list items
This commit is contained in:
parent
cf4048312e
commit
2daee794bc
3 changed files with 81 additions and 13 deletions
|
@ -1,14 +1,14 @@
|
||||||
class SimpleFormatComponent < ApplicationComponent
|
class SimpleFormatComponent < ApplicationComponent
|
||||||
# see: https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
|
# see: https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
|
||||||
REDCARPET_EXTENSIONS = {
|
REDCARPET_EXTENSIONS = {
|
||||||
no_intra_emphasis: false,
|
no_intra_emphasis: true,
|
||||||
|
disable_indented_code_blocks: true,
|
||||||
|
space_after_headers: true,
|
||||||
tables: false,
|
tables: false,
|
||||||
fenced_code_blocks: false,
|
fenced_code_blocks: false,
|
||||||
autolink: false,
|
autolink: false,
|
||||||
disable_indented_code_blocks: false,
|
|
||||||
strikethrough: false,
|
strikethrough: false,
|
||||||
lax_spacing: false,
|
lax_spacing: false,
|
||||||
space_after_headers: false,
|
|
||||||
superscript: false,
|
superscript: false,
|
||||||
underline: false,
|
underline: false,
|
||||||
highlight: false,
|
highlight: false,
|
||||||
|
@ -27,15 +27,28 @@ class SimpleFormatComponent < ApplicationComponent
|
||||||
def initialize(text, allow_a: true, class_names_map: {})
|
def initialize(text, allow_a: true, class_names_map: {})
|
||||||
@allow_a = allow_a
|
@allow_a = allow_a
|
||||||
|
|
||||||
@text = (text || "").gsub(/\R/, "\n\n") # force double \n otherwise a single one won't split paragraph
|
list_item = false
|
||||||
.split("\n\n")
|
lines = (text || "")
|
||||||
|
.lines
|
||||||
.map(&:lstrip) # this block prevent redcarpet to consider " text" as block code by lstriping
|
.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 = lines.map do |line|
|
||||||
@text = @text.gsub(SIMPLE_URL_REGEX) { _1.gsub('_', '\\_') } # Escape underscores in URLs
|
item_number = line.match(/\A(\d+)\./)
|
||||||
end
|
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(
|
@renderer = Redcarpet::Markdown.new(
|
||||||
Redcarpet::BareRenderer.new(class_names_map:),
|
Redcarpet::BareRenderer.new(class_names_map:),
|
||||||
|
@ -52,6 +65,6 @@ class SimpleFormatComponent < ApplicationComponent
|
||||||
end
|
end
|
||||||
|
|
||||||
def attributes
|
def attributes
|
||||||
['target', 'rel', 'href', 'class', 'title']
|
['target', 'rel', 'href', 'class', 'title', 'value']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,14 @@ module Redcarpet
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_item(content, list_type)
|
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/, '<br>')
|
||||||
|
attributes = item_number.present? ? { value: item_number[1] } : {}
|
||||||
|
|
||||||
|
content_tag(:li, text, attributes, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paragraph(text)
|
def paragraph(text)
|
||||||
|
|
|
@ -47,11 +47,59 @@ TEXT
|
||||||
<<~TEXT
|
<<~TEXT
|
||||||
1. 1er paragraphe
|
1. 1er paragraphe
|
||||||
2. paragraphe
|
2. paragraphe
|
||||||
|
4. 4eme paragraphe
|
||||||
TEXT
|
TEXT
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(page).to have_selector("ol", count: 1) }
|
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
|
end
|
||||||
|
|
||||||
context 'auto-link' do
|
context 'auto-link' do
|
||||||
|
|
Loading…
Reference in a new issue