diff --git a/app/assets/stylesheets/legislation_process.scss b/app/assets/stylesheets/legislation_process.scss index c605af7e7..a18266d97 100644 --- a/app/assets/stylesheets/legislation_process.scss +++ b/app/assets/stylesheets/legislation_process.scss @@ -459,6 +459,10 @@ padding: rem-calc(32) rem-calc(32) rem-calc(32) rem-calc(48); } + table { + @include table-scroll; + } + h2 { font-weight: 400; margin-bottom: rem-calc(32); diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a0ff6f621..1377b27d2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -10,26 +10,8 @@ module ApplicationHelper %i[ar fa he].include?(locale) end - def markdown(text) - return text if text.blank? - - # See https://github.com/vmg/redcarpet for options - render_options = { - filter_html: false, - hard_wrap: true, - link_attributes: { target: "_blank" } - } - renderer = Redcarpet::Render::HTML.new(render_options) - extensions = { - autolink: true, - fenced_code_blocks: true, - lax_spacing: true, - no_intra_emphasis: true, - strikethrough: true, - superscript: true - } - - AdminLegislationSanitizer.new.sanitize(Redcarpet::Markdown.new(renderer, extensions).render(text)) + def markdown(text, **render_options) + MarkdownConverter.new(text, **render_options).render end def wysiwyg(text) diff --git a/app/models/legislation/draft_version.rb b/app/models/legislation/draft_version.rb index c7576c421..b36cb688a 100644 --- a/app/models/legislation/draft_version.rb +++ b/app/models/legislation/draft_version.rb @@ -22,15 +22,11 @@ class Legislation::DraftVersion < ApplicationRecord scope :published, -> { where(status: "published").order("id DESC") } def body_html - renderer = Redcarpet::Render::HTML.new(with_toc_data: true) - - Redcarpet::Markdown.new(renderer).render(body) + MarkdownConverter.new(body, with_toc_data: true).render end def toc_html - renderer = Redcarpet::Render::HTML_TOC.new(with_toc_data: true) - - Redcarpet::Markdown.new(renderer).render(body) + MarkdownConverter.new(body).render_toc end def display_title diff --git a/lib/admin_legislation_sanitizer.rb b/lib/admin_legislation_sanitizer.rb index 103b305b6..358de0199 100644 --- a/lib/admin_legislation_sanitizer.rb +++ b/lib/admin_legislation_sanitizer.rb @@ -1,6 +1,6 @@ class AdminLegislationSanitizer < WYSIWYGSanitizer def allowed_tags - super + %w[img h1 h4 h5 h6] + super + %w[img h1 h4 h5 h6 table thead tbody tr th td] end def allowed_attributes diff --git a/lib/markdown_converter.rb b/lib/markdown_converter.rb new file mode 100644 index 000000000..d5d9845f0 --- /dev/null +++ b/lib/markdown_converter.rb @@ -0,0 +1,48 @@ +class MarkdownConverter + attr_reader :text, :render_options + + def initialize(text, **render_options) + @text = text + @render_options = render_options + end + + def render + return text if text.blank? + + AdminLegislationSanitizer.new.sanitize(Redcarpet::Markdown.new(renderer, extensions).render(text)) + end + + def render_toc + Redcarpet::Markdown.new(toc_renderer).render(text) + end + + private + + def renderer + Redcarpet::Render::HTML.new(default_render_options.merge(render_options)) + end + + def toc_renderer + Redcarpet::Render::HTML_TOC.new(with_toc_data: true) + end + + def default_render_options + { + filter_html: false, + hard_wrap: true, + link_attributes: { target: "_blank" } + } + end + + def extensions + { + autolink: true, + fenced_code_blocks: true, + lax_spacing: true, + no_intra_emphasis: true, + strikethrough: true, + superscript: true, + tables: true + } + end +end diff --git a/spec/factories/legislations.rb b/spec/factories/legislations.rb index 069899884..3c1dbe41b 100644 --- a/spec/factories/legislations.rb +++ b/spec/factories/legislations.rb @@ -127,6 +127,15 @@ FactoryBot.define do trait :final_version do final_version { true } end + + trait :with_table do + body { <<~BODY_MARKDOWN } + | id | name | age | gender | + |----|---------|-----|--------| + | 1 | Roberta | 39 | M | + | 2 | Oliver | 25 | F | + BODY_MARKDOWN + end end factory :legislation_annotation, class: "Legislation::Annotation" do diff --git a/spec/lib/admin_legislation_sanitizer_spec.rb b/spec/lib/admin_legislation_sanitizer_spec.rb new file mode 100644 index 000000000..bc467ba84 --- /dev/null +++ b/spec/lib/admin_legislation_sanitizer_spec.rb @@ -0,0 +1,65 @@ +require "rails_helper" + +describe AdminLegislationSanitizer do + let(:sanitizer) { AdminLegislationSanitizer.new } + + describe "#sanitize" do + it "allows images" do + html = 'DangerousSmile image' + expect(sanitizer.sanitize(html)).to eq(html) + end + + it "allows h1 to h6" do + html = '

Heading 1

+

Heading 2

+

Heading 3

+

Heading 4

+
Heading 5
+
Heading 6
' + expect(sanitizer.sanitize(html)).to eq(html) + end + + it "allows tables" do + html = ' + + + + + + + + + + + + + + + + + + + + + + +
idnameagegender
1Roberta39M
2Oliver25F
' + expect(sanitizer.sanitize(html)).to eq(html) + end + + it "allows alt src and id" do + html = 'DangerousSmile image' + expect(sanitizer.sanitize(html)).to eq(html) + end + + it "doesn't allow style" do + html = 'DangerousSmile image' + expect(sanitizer.sanitize(html)).not_to eq(html) + end + + it "doesn't allow class" do + html = 'DangerousSmile image' + expect(sanitizer.sanitize(html)).not_to eq(html) + end + end +end diff --git a/spec/models/legislation/draft_version_spec.rb b/spec/models/legislation/draft_version_spec.rb index 910a15f65..58123c837 100644 --- a/spec/models/legislation/draft_version_spec.rb +++ b/spec/models/legislation/draft_version_spec.rb @@ -29,6 +29,15 @@ describe Legislation::DraftVersion do expect(legislation_draft_version.toc_html).to eq(toc_html) end + it "renders the tables from the markdown body field" do + legislation_draft_version.body = body_with_table_markdown + + legislation_draft_version.save! + + expect(legislation_draft_version.body_html).to eq(body_with_table_html) + expect(legislation_draft_version.toc_html).to eq(toc_html) + end + def body_markdown <<~BODY_MARKDOWN # Title 1 @@ -50,6 +59,32 @@ describe Legislation::DraftVersion do BODY_MARKDOWN end + def body_with_table_markdown + <<~BODY_MARKDOWN + # Title 1 + + Some paragraph. + + A list: + + - item 1 + - item 2 + + ## Subtitle + + Another paragraph. + + # Title 2 + + Something about this. + + | id | name | age | gender | + |----|---------|-----|--------| + | 1 | Roberta | 39 | M | + | 2 | Oliver | 25 | F | + BODY_MARKDOWN + end + def body_html <<~BODY_HTML

Title 1

@@ -73,6 +108,54 @@ describe Legislation::DraftVersion do BODY_HTML end + def body_with_table_html + <<~BODY_HTML +

Title 1

+ +

Some paragraph.

+ +

A list:

+ + + +

Subtitle

+ +

Another paragraph.

+ +

Title 2

+ +

Something about this.

+ + + + + + + + + + + + + + + + + + + + + + + + +
idnameagegender
1Roberta39M
2Oliver25F
+ BODY_HTML + end + def toc_html <<~TOC_HTML