Merge branch 'issue-64070-asciidoctor-section-anchors' into 'master'
Enable section anchors Closes #64070 See merge request gitlab-org/gitlab-ce!30666
This commit is contained in:
commit
1e99c1b0a7
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Enable section anchors in Asciidoctor"
|
||||
merge_request: 30666
|
||||
author: Guillaume Grossetie
|
||||
type: added
|
|
@ -6,6 +6,9 @@ module Banzai
|
|||
#
|
||||
# Extends Banzai::Filter::BaseSanitizationFilter with specific rules.
|
||||
class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter
|
||||
# Section anchor link pattern
|
||||
SECTION_LINK_REF_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze
|
||||
|
||||
# Classes used by Asciidoctor to style components
|
||||
ADMONITION_CLASSES = %w(fa icon-note icon-tip icon-warning icon-caution icon-important).freeze
|
||||
CALLOUT_CLASSES = ['conum'].freeze
|
||||
|
@ -19,14 +22,17 @@ module Banzai
|
|||
td: ['icon'].freeze,
|
||||
i: ADMONITION_CLASSES + CALLOUT_CLASSES + CHECKLIST_CLASSES,
|
||||
ul: LIST_CLASSES,
|
||||
ol: LIST_CLASSES
|
||||
ol: LIST_CLASSES,
|
||||
a: ['anchor'].freeze
|
||||
}.freeze
|
||||
|
||||
ALLOWED_HEADERS = %w(h2 h3 h4 h5 h6).freeze
|
||||
|
||||
def customize_whitelist(whitelist)
|
||||
# Allow marks
|
||||
whitelist[:elements].push('mark')
|
||||
|
||||
# Allow any classes in `span`, `i`, `div`, `td`, `ul` and `ol` elements
|
||||
# Allow any classes in `span`, `i`, `div`, `td`, `ul`, `ol` and `a` elements
|
||||
# but then remove any unknown classes
|
||||
whitelist[:attributes]['span'] = %w(class)
|
||||
whitelist[:attributes]['div'].push('class')
|
||||
|
@ -34,12 +40,32 @@ module Banzai
|
|||
whitelist[:attributes]['i'] = %w(class)
|
||||
whitelist[:attributes]['ul'] = %w(class)
|
||||
whitelist[:attributes]['ol'] = %w(class)
|
||||
whitelist[:attributes]['a'].push('class')
|
||||
whitelist[:transformers].push(self.class.remove_element_classes)
|
||||
|
||||
# Allow `id` in heading elements for section anchors
|
||||
ALLOWED_HEADERS.each do |header|
|
||||
whitelist[:attributes][header] = %w(id)
|
||||
end
|
||||
whitelist[:transformers].push(self.class.remove_non_heading_ids)
|
||||
|
||||
whitelist
|
||||
end
|
||||
|
||||
class << self
|
||||
def remove_non_heading_ids
|
||||
lambda do |env|
|
||||
node = env[:node]
|
||||
|
||||
return unless ALLOWED_HEADERS.any?(node.name)
|
||||
return unless node.has_attribute?('id')
|
||||
|
||||
return if node['id'] =~ SECTION_LINK_REF_PATTERN
|
||||
|
||||
node.remove_attribute('id')
|
||||
end
|
||||
end
|
||||
|
||||
def remove_element_classes
|
||||
lambda do |env|
|
||||
node = env[:node]
|
||||
|
|
|
@ -13,6 +13,7 @@ module Gitlab
|
|||
MAX_INCLUDE_DEPTH = 5
|
||||
DEFAULT_ADOC_ATTRS = {
|
||||
'showtitle' => true,
|
||||
'sectanchors' => true,
|
||||
'idprefix' => 'user-content-',
|
||||
'idseparator' => '-',
|
||||
'env' => 'gitlab',
|
||||
|
|
|
@ -96,6 +96,75 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
context 'with passthrough' do
|
||||
it 'removes non heading ids' do
|
||||
input = <<~ADOC
|
||||
++++
|
||||
<h2 id="foo">Title</h2>
|
||||
++++
|
||||
ADOC
|
||||
|
||||
output = <<~HTML
|
||||
<h2>Title</h2>
|
||||
HTML
|
||||
|
||||
expect(render(input, context)).to include(output.strip)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with section anchors' do
|
||||
it 'preserves ids and links' do
|
||||
input = <<~ADOC
|
||||
= Title
|
||||
|
||||
== First section
|
||||
|
||||
This is the first section.
|
||||
|
||||
== Second section
|
||||
|
||||
This is the second section.
|
||||
|
||||
== Thunder ⚡ !
|
||||
|
||||
This is the third section.
|
||||
ADOC
|
||||
|
||||
output = <<~HTML
|
||||
<h1>Title</h1>
|
||||
<div>
|
||||
<h2 id="user-content-first-section">
|
||||
<a class="anchor" href="#user-content-first-section"></a>First section</h2>
|
||||
<div>
|
||||
<div>
|
||||
<p>This is the first section.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 id="user-content-second-section">
|
||||
<a class="anchor" href="#user-content-second-section"></a>Second section</h2>
|
||||
<div>
|
||||
<div>
|
||||
<p>This is the second section.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 id="user-content-thunder">
|
||||
<a class="anchor" href="#user-content-thunder"></a>Thunder ⚡ !</h2>
|
||||
<div>
|
||||
<div>
|
||||
<p>This is the third section.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
|
||||
expect(render(input, context)).to include(output.strip)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with checklist' do
|
||||
it 'preserves classes' do
|
||||
input = <<~ADOC
|
||||
|
|
Loading…
Reference in New Issue