diff --git a/app/javascript/controllers/mermaid_controller.js b/app/javascript/controllers/mermaid_controller.js index 7d29c2cc7e..b986cedce1 100644 --- a/app/javascript/controllers/mermaid_controller.js +++ b/app/javascript/controllers/mermaid_controller.js @@ -13,6 +13,7 @@ const MAX_PNG_TOTAL_PIXELS = 8_000_000; let mermaidInitialized = false; let mermaidLoadPromise = null; +let renderCount = 0; // Mermaid.js is not bundled with Redmine; administrators install it with // `bin/rails redmine:mermaid:install`. When it is available, javascript_heads @@ -91,7 +92,8 @@ export default class extends Controller { const container = document.createElement('div'); container.className = 'mermaid'; - container.textContent = this.element.textContent.trim(); + const source = this.element.textContent.trim(); + container.textContent = source; // Keep the source text in the container from flashing until Mermaid.js // replaces it with the diagram. Mermaid.js measures the diagram inside // the container, so it is made invisible rather than display: none, and @@ -104,7 +106,9 @@ export default class extends Controller { // On failure, show a Redmine flash message while keeping the source block // visible, so that the invalid diagram can be inspected and copied. - mermaid.run({ nodes: [container], suppressErrors: false }).then(() => { + mermaid.render(`redmine-mermaid-${++renderCount}`, source, container).then(({ svg, bindFunctions }) => { + container.innerHTML = svg; + bindFunctions?.(container); pre.style.display = 'none'; container.style.visibility = container.style.position = container.style.insetInline = ''; if (this.container === container) this.addDownloadLink(pre, container); diff --git a/test/system/mermaid_rendering_test.rb b/test/system/mermaid_rendering_test.rb index ecf12a62fd..3878722e77 100644 --- a/test/system/mermaid_rendering_test.rb +++ b/test/system/mermaid_rendering_test.rb @@ -56,6 +56,30 @@ class MermaidRenderingTest < ApplicationSystemTestCase end end + def test_should_render_multiple_mermaid_diagrams_with_unique_ids + skip unless mermaid_installed? + + issue = Issue.find(1) + issue.update_column( + :description, + [ + "graph TD;\nA-->B;", "graph TD;\nC-->D;", + "sequenceDiagram\nAlice->>Bob: Hi", "sequenceDiagram\nBob->>Alice: Hello", + "pie\n\"X\" : 1\n\"Y\" : 2", "classDiagram\nclass Foo" + ].map {|source| "```mermaid\n#{source}\n```"}.join("\n\n") + ) + + log_user('jsmith', 'jsmith') + visit "/issues/#{issue.id}" + + within('div.description') do + assert_selector 'div.mermaid svg', count: 6 + assert_no_selector 'svg .error-icon' + end + ids = page.evaluate_script("Array.from(document.querySelectorAll('div.mermaid svg')).map(svg => svg.id)") + assert_equal ids.uniq, ids + end + def test_should_show_flash_message_for_invalid_mermaid_diagram skip unless mermaid_installed?