amelioration(a11y): extrait un nouveau composant pour rendre du texte saisi par un humain accessible

This commit is contained in:
Martin 2023-02-22 05:00:42 +01:00 committed by mfo
parent 1e37e5ca60
commit 4d8b4e078b
6 changed files with 149 additions and 0 deletions

View file

@ -70,6 +70,7 @@ gem 'rack-attack'
gem 'rails' gem 'rails'
gem 'rails-i18n' # Locales par défaut gem 'rails-i18n' # Locales par défaut
gem 'rake-progressbar', require: false gem 'rake-progressbar', require: false
gem 'redcarpet'
gem 'rexml' # add missing gem due to ruby3 (https://github.com/Shopify/bootsnap/issues/325) gem 'rexml' # add missing gem due to ruby3 (https://github.com/Shopify/bootsnap/issues/325)
gem 'rgeo-geojson' gem 'rgeo-geojson'
gem 'rqrcode' gem 'rqrcode'

View file

@ -573,6 +573,7 @@ GEM
rb-fsevent (0.10.4) rb-fsevent (0.10.4)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
redcarpet (3.6.0)
regexp_parser (2.6.0) regexp_parser (2.6.0)
request_store (1.5.0) request_store (1.5.0)
rack (>= 1.4) rack (>= 1.4)
@ -909,6 +910,7 @@ DEPENDENCIES
rails-erd rails-erd
rails-i18n rails-i18n
rake-progressbar rake-progressbar
redcarpet
rexml rexml
rgeo-geojson rgeo-geojson
rqrcode rqrcode

View file

@ -0,0 +1,51 @@
class SimpleFormatComponent < ApplicationComponent
# see: https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
REDCARPET_EXTENSIONS = {
no_intra_emphasis: false,
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,
quote: false,
footnotes: false
}
# see: https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
REDCARPET_RENDERER_OPTS = {
no_images: true
}
def initialize(text, allow_a: true, class_names_map: {})
@text = (text || "").gsub(/\R/, "\n\n") # force double \n otherwise a single one won't split paragraph
.split("\n\n") #
.map(&:lstrip) # this block prevent redcarpet to consider " text" as block code by lstriping
.join("\n\n") #
@allow_a = allow_a
@renderer = Redcarpet::Markdown.new(
Redcarpet::BareRenderer.new(link_attributes: external_link_attributes, class_names_map: class_names_map),
REDCARPET_EXTENSIONS.merge(autolink: @allow_a)
)
end
def external_link_attributes
{ target: '_blank', rel: 'noopener noreferrer' }
end
def tags
if @allow_a
Rails.configuration.action_view.sanitized_allowed_tags + ['a']
else
Rails.configuration.action_view.sanitized_allowed_tags
end
end
def attributes
['target', 'rel', 'href', 'class']
end
end

View file

@ -0,0 +1 @@
= sanitize(@renderer.render(@text), tags:, attributes:)

View file

@ -0,0 +1,21 @@
module Redcarpet
class BareRenderer < Redcarpet::Render::HTML
include ActionView::Helpers::TagHelper
# won't use rubocop tag method because it is missing output buffer
# rubocop:disable Rails/ContentTag
def list(content, list_type)
tag = list_type == :ordered ? :ol : :ul
content_tag(tag, content, { class: @options[:class_names_map].fetch(:list) {} }, false)
end
def list_item(content, list_type)
content_tag(:li, content.strip.gsub(/<\/?p>/, ''), {}, false)
end
def paragraph(text)
content_tag(:p, text, { class: @options[:class_names_map].fetch(:paragraph) {} }, false)
end
# rubocop:enable Rails/ContentTag
end
end

View file

@ -0,0 +1,73 @@
describe SimpleFormatComponent, type: :component do
let(:allow_a) { false }
before { render_inline(described_class.new(text, allow_a: allow_a)) }
context 'one line' do
let(:text) do
"1er paragraphe"
end
it { expect(page).to have_selector("p", count: 1, text: text) }
end
context 'one with leading spaces' do
let(:text) do
<<-TEXT
1er paragraphe
TEXT
end
it { expect(page).to have_selector("p", count: 1, text: text.strip) }
end
context 'two lines' do
let(:text) do
<<~TEXT
1er paragraphe
2eme paragraphe
TEXT
end
it { expect(page).to have_selector("p", count: 2) }
it { text.split("\n").map(&:strip).map { expect(page).to have_text(_1) } }
end
context 'unordered list items' do
let(:text) do
<<~TEXT
- 1er paragraphe
- paragraphe
TEXT
end
it { expect(page).to have_selector("ul", count: 1) }
it { expect(page).to have_selector("li", count: 2) }
end
context 'ordered list items' do
let(:text) do
<<~TEXT
1. 1er paragraphe
2. paragraphe
TEXT
end
it { expect(page).to have_selector("ol", count: 1) }
it { expect(page).to have_selector("li", count: 2) }
end
context 'auto-link' do
let(:text) do
<<~TEXT
bonjour https://www.demarches-simplifiees.fr
TEXT
end
context 'enabled' do
let(:allow_a) { true }
it { expect(page).to have_selector("a") }
end
context 'disabled' do
it { expect(page).not_to have_selector("a") }
end
end
end