From 288b276077987bc77f191d2cb93eb2f764c5c1ef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 20 Dec 2017 14:48:09 +0100 Subject: [PATCH] Copy Mermaid graphs as GFM --- .../javascripts/behaviors/copy_as_gfm.js | 12 +++ app/assets/javascripts/render_mermaid.js | 20 +++- lib/banzai/filter/mermaid_filter.rb | 11 +-- spec/features/copy_as_gfm_spec.rb | 96 +++++++++++++++++++ spec/features/markdown_spec.rb | 2 +- spec/lib/banzai/filter/mermaid_filter_spec.rb | 4 +- 6 files changed, 131 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/behaviors/copy_as_gfm.js b/app/assets/javascripts/behaviors/copy_as_gfm.js index e7dc4ef8304..c6eca72c51b 100644 --- a/app/assets/javascripts/behaviors/copy_as_gfm.js +++ b/app/assets/javascripts/behaviors/copy_as_gfm.js @@ -74,6 +74,18 @@ const gfmRules = { return `![${el.dataset.title}](${el.getAttribute('src')})`; }, }, + MermaidFilter: { + 'svg.mermaid'(el, text) { + const sourceEl = el.querySelector('text.source'); + if (!sourceEl) return false; + + return `\`\`\`mermaid\n${CopyAsGFM.nodeToGFM(sourceEl)}\n\`\`\``; + }, + 'svg.mermaid style, svg.mermaid g'(el, text) { + // We don't want to include the content of these elements in the copied text. + return ''; + }, + }, MathFilter: { 'pre.code.math[data-math-style=display]'(el, text) { return `\`\`\`math\n${text.trim()}\n\`\`\``; diff --git a/app/assets/javascripts/render_mermaid.js b/app/assets/javascripts/render_mermaid.js index 41942c04a4e..b7cde6fb092 100644 --- a/app/assets/javascripts/render_mermaid.js +++ b/app/assets/javascripts/render_mermaid.js @@ -24,7 +24,25 @@ export default function renderMermaid($els) { }); $els.each((i, el) => { - mermaid.init(undefined, el); + const source = el.textContent; + + mermaid.init(undefined, el, (id) => { + const svg = document.getElementById(id); + + svg.classList.add('mermaid'); + + // pre > code > svg + svg.closest('pre').replaceWith(svg); + + // We need to add the original source into the DOM to allow Copy-as-GFM + // to access it. + const sourceEl = document.createElement('text'); + sourceEl.classList.add('source'); + sourceEl.setAttribute('display', 'none'); + sourceEl.textContent = source; + + svg.appendChild(sourceEl); + }); }); }).catch((err) => { Flash(`Can't load mermaid module: ${err}`); diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb index b545b947a2c..65c131e08d9 100644 --- a/lib/banzai/filter/mermaid_filter.rb +++ b/lib/banzai/filter/mermaid_filter.rb @@ -2,16 +2,7 @@ module Banzai module Filter class MermaidFilter < HTML::Pipeline::Filter def call - doc.css('pre[lang="mermaid"]').add_class('mermaid') - doc.css('pre[lang="mermaid"]').add_class('js-render-mermaid') - - # The `` blocks are added in the lib/banzai/filter/syntax_highlight_filter.rb - # We want to keep context and consistency, so we the blocks are added for all filters. - # Details: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107/diffs?diff_id=7962900#note_45495859 - doc.css('pre[lang="mermaid"]').each do |pre| - document = pre.at('code') - document.replace(document.content) - end + doc.css('pre[lang="mermaid"] > code').add_class('js-render-mermaid') doc end diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb index 1fcb8d5bc67..d8f1a919522 100644 --- a/spec/features/copy_as_gfm_spec.rb +++ b/spec/features/copy_as_gfm_spec.rb @@ -284,6 +284,102 @@ describe 'Copy as GFM', :js do expect(output_gfm.strip).to eq(gfm.strip) end + verify( + 'MermaidFilter: mermaid as converted from GFM to HTML', + + <<-GFM.strip_heredoc + ```mermaid + graph TD; + A-->B; + ``` + GFM + ) + + aggregate_failures('MermaidFilter: mermaid as transformed from HTML to SVG') do + gfm = <<-GFM.strip_heredoc + ```mermaid + graph TD; + A-->B; + ``` + GFM + + html = <<-HTML.strip_heredoc + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + + + + +
A
+
+
+
+
+ + + + + + +
B
+
+
+
+
+
+
+
+ graph TD; + A-->B; + +
+ HTML + + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + verify( 'SanitizationFilter', diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index e285befc66f..a2b78a5e021 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -71,7 +71,7 @@ describe 'GitLab Markdown' do it 'parses mermaid code block' do aggregate_failures do - expect(doc).to have_selector('pre.code.js-render-mermaid') + expect(doc).to have_selector('pre[lang=mermaid] > code.js-render-mermaid') end end diff --git a/spec/lib/banzai/filter/mermaid_filter_spec.rb b/spec/lib/banzai/filter/mermaid_filter_spec.rb index 532d25e121d..f6474c8936d 100644 --- a/spec/lib/banzai/filter/mermaid_filter_spec.rb +++ b/spec/lib/banzai/filter/mermaid_filter_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' describe Banzai::Filter::MermaidFilter do include FilterSpecHelper - it 'adds `js-render-mermaid` class to the `pre` tag' do + it 'adds `js-render-mermaid` class to the `code` tag' do doc = filter("
graph TD;\n  A-->B;\n
") - result = doc.xpath('descendant-or-self::pre').first + result = doc.css('code').first expect(result[:class]).to include('js-render-mermaid') end