From 853d9b7f981fde279dafc7f32e931579f75b6a48 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Tue, 14 Mar 2023 13:52:20 +0100 Subject: [PATCH 01/11] #4220 Create text utility functions handling new lines and applying them on mindmap --- cypress/platform/knsv2.html | 15 +- .../mermaid/src/dagre-wrapper/createLabel.js | 8 +- .../mermaid/src/diagrams/mindmap/mindmapDb.js | 2 +- .../src/diagrams/mindmap/parser/mindmap.jison | 4 + .../mermaid/src/diagrams/mindmap/svgDraw.js | 51 ++++-- .../mermaid/src/rendering-util/createText.js | 161 ++++++++++++++++++ 6 files changed, 219 insertions(+), 22 deletions(-) create mode 100644 packages/mermaid/src/rendering-util/createText.js diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index fccd65004..36f481a08 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -54,7 +54,7 @@ -
+    
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 graph BT
 a{The cat in the hat} -- 1o --> b
@@ -66,12 +66,13 @@ h --3i -->a
 b --> d(The dog in the hog)
 c --> d
     
-
-flowchart-elk TB
-      a --> b
-      a --> c
-      b --> d
-      c --> d
+    
+mindmap
+    id1["`Start
+second line 😎`"]
+      id2[Child]
+      id3[Child]
+      id4[Child]
     
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
diff --git a/packages/mermaid/src/dagre-wrapper/createLabel.js b/packages/mermaid/src/dagre-wrapper/createLabel.js
index af5032096..ff7834c4f 100644
--- a/packages/mermaid/src/dagre-wrapper/createLabel.js
+++ b/packages/mermaid/src/dagre-wrapper/createLabel.js
@@ -41,7 +41,13 @@ function addHtmlLabel(node) {
   div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
   return fo.node();
 }
-
+/**
+ * @param _vertexText
+ * @param style
+ * @param isTitle
+ * @param isNode
+ * @deprecated svg-util/createText instead
+ */
 const createLabel = (_vertexText, style, isTitle, isNode) => {
   let vertexText = _vertexText || '';
   if (typeof vertexText === 'object') {
diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapDb.js b/packages/mermaid/src/diagrams/mindmap/mindmapDb.js
index 71aa449d9..7585029cf 100644
--- a/packages/mermaid/src/diagrams/mindmap/mindmapDb.js
+++ b/packages/mermaid/src/diagrams/mindmap/mindmapDb.js
@@ -33,7 +33,7 @@ export const addNode = (level, id, descr, type) => {
     id: cnt++,
     nodeId: sanitizeText(id),
     level,
-    descr: sanitizeText(descr),
+    descr: sanitizeText(descr).replace(/\n/g, '
'), type, children: [], width: getConfig().mindmap.maxNodeWidth, diff --git a/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison b/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison index d2f6bbf1a..84a6191cf 100644 --- a/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison +++ b/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison @@ -12,6 +12,7 @@ %} %x NODE %x NSTR +%x NSTR2 %x ICON %x CLASS @@ -41,6 +42,9 @@ // !(-\() return 'NODE_ID'; [^\(\[\n\-\)\{\}]+ return 'NODE_ID'; <> return 'EOF'; +["][`] { this.begin("NSTR2");} +[^`"]+ { return "NODE_DESCR";} +[`]["] { this.popState();} ["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");} [^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";} ["] {this.popState();} diff --git a/packages/mermaid/src/diagrams/mindmap/svgDraw.js b/packages/mermaid/src/diagrams/mindmap/svgDraw.js index 2b1aa021e..2c3dcca56 100644 --- a/packages/mermaid/src/diagrams/mindmap/svgDraw.js +++ b/packages/mermaid/src/diagrams/mindmap/svgDraw.js @@ -1,5 +1,6 @@ import { select } from 'd3'; import * as db from './mindmapDb'; +import { createText, setSize } from '../../rendering-util/createText'; const MAX_SECTIONS = 12; /** @@ -11,7 +12,7 @@ function wrap(text, width) { var text = select(this), words = text .text() - .split(/(\s+|
)/) + .split(/(\s+|)/) .reverse(), word, line = [], @@ -28,10 +29,10 @@ function wrap(text, width) { word = words[words.length - 1 - j]; line.push(word); tspan.text(line.join(' ').trim()); - if (tspan.node().getComputedTextLength() > width || word === '
') { + if (tspan.node().getComputedTextLength() > width || word === '
') { line.pop(); tspan.text(line.join(' ').trim()); - if (word === '
') { + if (word === '
') { line = ['']; } else { line = [word]; @@ -203,6 +204,7 @@ const roundedRectBkg = function (elem, node) { * @returns {number} The height nodes dom element */ export const drawNode = function (elem, node, fullSection, conf) { + const htmlLabels = false; const section = fullSection % (MAX_SECTIONS - 1); const nodeElem = elem.append('g'); node.section = section; @@ -215,15 +217,29 @@ export const drawNode = function (elem, node, fullSection, conf) { // Create the wrapped text element const textElem = nodeElem.append('g'); - const txt = textElem - .append('text') - .text(node.descr) - .attr('dy', '1em') - .attr('alignment-baseline', 'middle') - .attr('dominant-baseline', 'middle') - .attr('text-anchor', 'middle') - .call(wrap, node.width); - const bbox = txt.node().getBBox(); + + const newEl = createText(textElem, node.descr, { useHtmlLabels: htmlLabels }); + const txt = textElem.node().appendChild(newEl); + // const txt = textElem.append(newEl); + // const txt = textElem + // .append('text') + // .text(node.descr) + // .attr('dy', '1em') + // .attr('alignment-baseline', 'middle') + // .attr('dominant-baseline', 'middle') + // .attr('text-anchor', 'middle') + // .call(wrap, node.width); + // const newerEl = textElem.node().appendChild(newEl); + // setSize(textElem); + if (!htmlLabels) { + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'middle') + .attr('text-anchor', 'middle'); + } + // .call(wrap, node.width); + const bbox = textElem.node().getBBox(); const fontSize = conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize; node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding; node.width = bbox.width + 2 * node.padding; @@ -267,7 +283,16 @@ export const drawNode = function (elem, node, fullSection, conf) { ); } } else { - textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')'); + if (!htmlLabels) { + const dx = node.width / 2; + const dy = node.padding / 2; + textElem.attr('transform', 'translate(' + dx + ', ' + dy + ')'); + // textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')'); + } else { + const dx = (node.width - bbox.width) / 2; + const dy = (node.height - bbox.height) / 2; + textElem.attr('transform', 'translate(' + dx + ', ' + dy + ')'); + } } switch (node.type) { diff --git a/packages/mermaid/src/rendering-util/createText.js b/packages/mermaid/src/rendering-util/createText.js new file mode 100644 index 000000000..58e0f54a7 --- /dev/null +++ b/packages/mermaid/src/rendering-util/createText.js @@ -0,0 +1,161 @@ +import { select } from 'd3'; +import { log } from '../logger'; +import { getConfig } from '../config'; +import { evaluate } from '../diagrams/common/common'; +import { decodeEntities } from '../mermaidAPI'; + +/** + * @param dom + * @param styleFn + */ +function applyStyle(dom, styleFn) { + if (styleFn) { + dom.attr('style', styleFn); + } +} + +/** + * @param element + * @param {any} node + * @returns {SVGForeignObjectElement} Node + */ +function addHtmlSpan(element, node) { + const fo = element.append('foreignObject'); + const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'); + const div = fo.append('xhtml:div'); + + const label = node.label; + const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel'; + div.html( + '' + + label + + '' + ); + + applyStyle(div, node.labelStyle); + div.style('display', 'inline-block'); + const bbox = div.node().getBoundingClientRect(); + fo.style('width', bbox.width); + fo.style('height', bbox.height); + + const divNode = div.node(); + window.divNode = divNode; + // Fix for firefox + div.style('white-space', 'nowrap'); + div.attr('xmlns', 'http://www.w3.org/1999/xhtml'); + return fo.node(); +} + +/** + * @param {string} text The text to be wrapped + * @param {number} width The max width of the text + */ +function wrap(text, width) { + text.each(function () { + var text = select(this), + words = text + .text() + .split(/(\s+|)/) + .reverse(), + word, + line = [], + lineHeight = 1.1, // ems + y = text.attr('y'), + dy = parseFloat(text.attr('dy')), + tspan = text + .text(null) + .append('tspan') + .attr('x', 0) + .attr('y', y) + .attr('dy', dy + 'em'); + for (let j = 0; j < words.length; j++) { + word = words[words.length - 1 - j]; + line.push(word); + tspan.text(line.join(' ').trim()); + if (tspan.node().getComputedTextLength() > width || word === '
') { + line.pop(); + tspan.text(line.join(' ').trim()); + if (word === '
') { + line = ['']; + } else { + line = [word]; + } + + tspan = text + .append('tspan') + .attr('x', 0) + .attr('y', y) + .attr('dy', lineHeight + 'em') + .text(word); + } + } + }); +} + +/** + * + * @param el + * @param {*} text + * @param {*} param1 + * @param root0 + * @param root0.style + * @param root0.isTitle + * @param root0.classes + * @param root0.useHtmlLabels + * @param root0.isNode + * @returns + */ +// Note when using from flowcharts converting the API isNode means classes should be set accordingly. When using htmlLabels => to sett classes to'nodeLabel' when isNode=true otherwise 'edgeLabel' +// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row' +export const createText = ( + el, + text = '', + { style = '', isTitle = false, classes = '', useHtmlLabels = true, isNode = true } = {} +) => { + if (useHtmlLabels) { + // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? + text = text.replace(/\\n|\n/g, '
'); + log.info('text' + text); + const node = { + isNode, + label: decodeEntities(text).replace( + /fa[blrs]?:fa-[\w-]+/g, + (s) => `` + ), + labelStyle: style.replace('fill:', 'color:'), + }; + let vertexNode = addHtmlSpan(el, node); + return vertexNode; + } else { + const svgText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + svgText.setAttribute('style', style.replace('color:', 'fill:')); + // el.attr('style', style.replace('color:', 'fill:')); + let rows = []; + if (typeof text === 'string') { + rows = text.split(/\\n|\n|/gi); + } else if (Array.isArray(text)) { + rows = text; + } else { + rows = []; + } + + for (const row of rows) { + const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); + tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); + tspan.setAttribute('dy', '1em'); + tspan.setAttribute('x', '0'); + if (isTitle) { + tspan.setAttribute('class', 'title-row'); + } else { + tspan.setAttribute('class', 'row'); + } + tspan.textContent = row.trim(); + svgText.appendChild(tspan); + } + return svgText; + } +}; From a1c50b8079950e44e2cae19620ce92f63d46b17d Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Mon, 20 Mar 2023 14:15:26 +0100 Subject: [PATCH 02/11] #4220 Parsing the text as markdown and rendering accordingly --- cypress/platform/knsv2.html | 12 +- packages/mermaid/package.json | 1 + .../mermaid/src/diagrams/mindmap/mindmapDb.js | 2 +- .../mermaid/src/diagrams/mindmap/svgDraw.js | 7 +- .../mermaid/src/rendering-util/createText.js | 144 +++++++------- .../rendering-util/handle-markdown-text.js | 78 ++++++++ .../handle-markdown-text.spec.js | 181 ++++++++++++++++++ pnpm-lock.yaml | 93 ++++++++- 8 files changed, 434 insertions(+), 84 deletions(-) create mode 100644 packages/mermaid/src/rendering-util/handle-markdown-text.js create mode 100644 packages/mermaid/src/rendering-util/handle-markdown-text.spec.js diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 36f481a08..923529074 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -67,6 +67,14 @@ b --> d(The dog in the hog) c --> d
+mindmap
+    id1["`Start`"]
+      id2["`Child **with bold** text`"]
+      id3["`Children of which some
+      is using *italic type of* text`"]
+      id4[Child]
+    
+
 mindmap
     id1["`Start
 second line 😎`"]
@@ -273,12 +281,14 @@ mindmap
       mermaid.initialize({
         theme: 'forest',
         startOnLoad: true,
-        logLevel: 5,
+        logLevel: 0,
         flowchart: {
           // defaultRenderer: 'elk',
           useMaxWidth: false,
+          // htmlLabels: false,
           htmlLabels: true,
         },
+        htmlLabels: true,
         gantt: {
           useMaxWidth: false,
         },
diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json
index 3c9ff1ed4..b62000937 100644
--- a/packages/mermaid/package.json
+++ b/packages/mermaid/package.json
@@ -53,6 +53,7 @@
   },
   "dependencies": {
     "@braintree/sanitize-url": "^6.0.0",
+    "@khanacademy/simple-markdown": "^0.8.6",
     "cytoscape": "^3.23.0",
     "cytoscape-cose-bilkent": "^4.1.0",
     "cytoscape-fcose": "^2.1.0",
diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapDb.js b/packages/mermaid/src/diagrams/mindmap/mindmapDb.js
index 7585029cf..71aa449d9 100644
--- a/packages/mermaid/src/diagrams/mindmap/mindmapDb.js
+++ b/packages/mermaid/src/diagrams/mindmap/mindmapDb.js
@@ -33,7 +33,7 @@ export const addNode = (level, id, descr, type) => {
     id: cnt++,
     nodeId: sanitizeText(id),
     level,
-    descr: sanitizeText(descr).replace(/\n/g, '
'), + descr: sanitizeText(descr), type, children: [], width: getConfig().mindmap.maxNodeWidth, diff --git a/packages/mermaid/src/diagrams/mindmap/svgDraw.js b/packages/mermaid/src/diagrams/mindmap/svgDraw.js index 2c3dcca56..5394e004e 100644 --- a/packages/mermaid/src/diagrams/mindmap/svgDraw.js +++ b/packages/mermaid/src/diagrams/mindmap/svgDraw.js @@ -1,6 +1,6 @@ import { select } from 'd3'; import * as db from './mindmapDb'; -import { createText, setSize } from '../../rendering-util/createText'; +import { createText } from '../../rendering-util/createText'; const MAX_SECTIONS = 12; /** @@ -217,9 +217,8 @@ export const drawNode = function (elem, node, fullSection, conf) { // Create the wrapped text element const textElem = nodeElem.append('g'); - - const newEl = createText(textElem, node.descr, { useHtmlLabels: htmlLabels }); - const txt = textElem.node().appendChild(newEl); + const newEl = createText(textElem, node.descr, { useHtmlLabels: htmlLabels, width: node.width }); + // const txt = textElem.node().appendChild(newEl); // const txt = textElem.append(newEl); // const txt = textElem // .append('text') diff --git a/packages/mermaid/src/rendering-util/createText.js b/packages/mermaid/src/rendering-util/createText.js index 58e0f54a7..47e0776e4 100644 --- a/packages/mermaid/src/rendering-util/createText.js +++ b/packages/mermaid/src/rendering-util/createText.js @@ -3,7 +3,7 @@ import { log } from '../logger'; import { getConfig } from '../config'; import { evaluate } from '../diagrams/common/common'; import { decodeEntities } from '../mermaidAPI'; - +import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text'; /** * @param dom * @param styleFn @@ -51,51 +51,79 @@ function addHtmlSpan(element, node) { } /** - * @param {string} text The text to be wrapped - * @param {number} width The max width of the text + * Creates a tspan element with the specified attributes for text positioning. + * + * @param {object} textElement - The parent text element to append the tspan element. + * @param {number} lineIndex - The index of the current line in the structuredText array. + * @param {number} lineHeight - The line height value for the text. + * @returns {object} The created tspan element. */ -function wrap(text, width) { - text.each(function () { - var text = select(this), - words = text - .text() - .split(/(\s+|)/) - .reverse(), - word, - line = [], - lineHeight = 1.1, // ems - y = text.attr('y'), - dy = parseFloat(text.attr('dy')), - tspan = text - .text(null) - .append('tspan') - .attr('x', 0) - .attr('y', y) - .attr('dy', dy + 'em'); - for (let j = 0; j < words.length; j++) { - word = words[words.length - 1 - j]; - line.push(word); - tspan.text(line.join(' ').trim()); - if (tspan.node().getComputedTextLength() > width || word === '
') { - line.pop(); - tspan.text(line.join(' ').trim()); - if (word === '
') { - line = ['']; - } else { - line = [word]; - } +function createTspan(textElement, lineIndex, lineHeight) { + return textElement + .append('tspan') + .attr('x', 0) + .attr('y', lineIndex * lineHeight + 'em') + .attr('dy', lineHeight + 'em'); +} - tspan = text - .append('tspan') - .attr('x', 0) - .attr('y', y) - .attr('dy', lineHeight + 'em') - .text(word); +/** + * Creates a formatted text element by breaking lines and applying styles based on + * the given structuredText. + * + * @param {number} width - The maximum allowed width of the text. + * @param {object} g - The parent group element to append the formatted text. + * @param {Array} structuredText - The structured text data to format. + */ +function createFormattedText(width, g, structuredText) { + const lineHeight = 1.1; + + const textElement = g.append('text'); + + structuredText.forEach((line, lineIndex) => { + let tspan = createTspan(textElement, lineIndex, lineHeight); + + let words = [...line].reverse(); + let currentWord; + let wrappedLine = []; + + while (words.length) { + currentWord = words.pop(); + wrappedLine.push(currentWord); + + updateTextContentAndStyles(tspan, wrappedLine); + + if (tspan.node().getComputedTextLength() > width) { + wrappedLine.pop(); + words.push(currentWord); + + updateTextContentAndStyles(tspan, wrappedLine); + + wrappedLine = []; + tspan = createTspan(textElement, ++lineIndex, lineHeight); } } }); } +/** + * Updates the text content and styles of the given tspan element based on the + * provided wrappedLine data. + * + * @param {object} tspan - The tspan element to update. + * @param {Array} wrappedLine - The line data to apply to the tspan element. + */ +function updateTextContentAndStyles(tspan, wrappedLine) { + tspan.text(''); + + wrappedLine.forEach((word) => { + tspan + .append('tspan') + .attr('font-style', word.type === 'em' ? 'italic' : 'normal') + .attr('font-weight', word.type === 'strong' ? 'bold' : 'normal') + .text(word.content + ' '); + }); +} + /** * * @param el @@ -114,15 +142,17 @@ function wrap(text, width) { export const createText = ( el, text = '', - { style = '', isTitle = false, classes = '', useHtmlLabels = true, isNode = true } = {} + { style = '', isTitle = false, classes = '', useHtmlLabels = true, isNode = true, width } = {} ) => { + log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode); if (useHtmlLabels) { // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? - text = text.replace(/\\n|\n/g, '
'); - log.info('text' + text); + // text = text.replace(/\\n|\n/g, '
'); + const htmlText = markdownToHTML(text); + // log.info('markdo wnToHTML' + text, markdownToHTML(text)); const node = { isNode, - label: decodeEntities(text).replace( + label: decodeEntities(htmlText).replace( /fa[blrs]?:fa-[\w-]+/g, (s) => `` ), @@ -131,31 +161,7 @@ export const createText = ( let vertexNode = addHtmlSpan(el, node); return vertexNode; } else { - const svgText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); - svgText.setAttribute('style', style.replace('color:', 'fill:')); - // el.attr('style', style.replace('color:', 'fill:')); - let rows = []; - if (typeof text === 'string') { - rows = text.split(/\\n|\n|/gi); - } else if (Array.isArray(text)) { - rows = text; - } else { - rows = []; - } - - for (const row of rows) { - const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); - tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); - tspan.setAttribute('dy', '1em'); - tspan.setAttribute('x', '0'); - if (isTitle) { - tspan.setAttribute('class', 'title-row'); - } else { - tspan.setAttribute('class', 'row'); - } - tspan.textContent = row.trim(); - svgText.appendChild(tspan); - } - return svgText; + const structuredText = markdownToLines(text); + return createFormattedText(width, el, structuredText); } }; diff --git a/packages/mermaid/src/rendering-util/handle-markdown-text.js b/packages/mermaid/src/rendering-util/handle-markdown-text.js new file mode 100644 index 000000000..c4d10d9dc --- /dev/null +++ b/packages/mermaid/src/rendering-util/handle-markdown-text.js @@ -0,0 +1,78 @@ +import SimpleMarkdown from '@khanacademy/simple-markdown'; + +/** + * + * @param markdown + */ +export function markdownToLines(markdown) { + const mdParse = SimpleMarkdown.defaultBlockParse; + const syntaxTree = mdParse(markdown); + + let lines = [[]]; + let currentLine = 0; + + /** + * + * @param node + * @param parentType + */ + function processNode(node, parentType) { + if (node.type === 'text') { + const textLines = node.content.split('\n'); + textLines.forEach((textLine, index) => { + if (index !== 0) { + currentLine++; + lines.push([]); + } + textLine.split(' ').forEach((word) => { + if (word) { + lines[currentLine].push({ content: word, type: parentType || 'normal' }); + } + }); + }); + } else if (node.type === 'strong' || node.type === 'em') { + node.content.forEach((contentNode) => { + processNode(contentNode, node.type); + }); + } + } + + syntaxTree.forEach((treeNode) => { + if (treeNode.type === 'paragraph') { + treeNode.content.forEach((contentNode) => { + processNode(contentNode); + }); + } + }); + + return lines; +} + +/** + * + * @param markdown + */ +export function markdownToHTML(markdown) { + const mdParse = SimpleMarkdown.defaultBlockParse; + const syntaxTree = mdParse(markdown); + + /** + * + * @param node + */ + function output(node) { + if (node.type === 'text') { + return node.content.replace(/\n/g, '
'); + } else if (node.type === 'strong') { + return `${node.content.map(output).join('')}`; + } else if (node.type === 'em') { + return `${node.content.map(output).join('')}`; + } else if (node.type === 'paragraph') { + return `

${node.content.map(output).join('')}

`; + } else { + return ''; + } + } + + return syntaxTree.map(output).join(''); +} diff --git a/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js b/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js new file mode 100644 index 000000000..db542543a --- /dev/null +++ b/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js @@ -0,0 +1,181 @@ +// import { test } from 'vitest'; +import { markdownToLines, markdownToHTML } from './handle-markdown-text'; +import { test } from 'vitest'; + +test('markdownToLines - Basic test', () => { + const input = `This is regular text +Here is a new line +There is some words **with a bold** section +Here is a line *with an italic* section`; + + const expectedOutput = [ + [ + { content: 'This', type: 'normal' }, + { content: 'is', type: 'normal' }, + { content: 'regular', type: 'normal' }, + { content: 'text', type: 'normal' }, + ], + [ + { content: 'Here', type: 'normal' }, + { content: 'is', type: 'normal' }, + { content: 'a', type: 'normal' }, + { content: 'new', type: 'normal' }, + { content: 'line', type: 'normal' }, + ], + [ + { content: 'There', type: 'normal' }, + { content: 'is', type: 'normal' }, + { content: 'some', type: 'normal' }, + { content: 'words', type: 'normal' }, + { content: 'with', type: 'strong' }, + { content: 'a', type: 'strong' }, + { content: 'bold', type: 'strong' }, + { content: 'section', type: 'normal' }, + ], + [ + { content: 'Here', type: 'normal' }, + { content: 'is', type: 'normal' }, + { content: 'a', type: 'normal' }, + { content: 'line', type: 'normal' }, + { content: 'with', type: 'em' }, + { content: 'an', type: 'em' }, + { content: 'italic', type: 'em' }, + { content: 'section', type: 'normal' }, + ], + ]; + + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToLines - Empty input', () => { + const input = ''; + const expectedOutput = [[]]; + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToLines - No formatting', () => { + const input = `This is a simple test +with no formatting`; + + const expectedOutput = [ + [ + { content: 'This', type: 'normal' }, + { content: 'is', type: 'normal' }, + { content: 'a', type: 'normal' }, + { content: 'simple', type: 'normal' }, + { content: 'test', type: 'normal' }, + ], + [ + { content: 'with', type: 'normal' }, + { content: 'no', type: 'normal' }, + { content: 'formatting', type: 'normal' }, + ], + ]; + + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToLines - Only bold formatting', () => { + const input = `This is a **bold** test`; + + const expectedOutput = [ + [ + { content: 'This', type: 'normal' }, + { content: 'is', type: 'normal' }, + { content: 'a', type: 'normal' }, + { content: 'bold', type: 'strong' }, + { content: 'test', type: 'normal' }, + ], + ]; + + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToLines - Only italic formatting', () => { + const input = `This is an *italic* test`; + + const expectedOutput = [ + [ + { content: 'This', type: 'normal' }, + { content: 'is', type: 'normal' }, + { content: 'an', type: 'normal' }, + { content: 'italic', type: 'em' }, + { content: 'test', type: 'normal' }, + ], + ]; + + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + +it('markdownToLines - Mixed formatting', () => { + const input = `*Italic* and **bold** formatting`; + + const expectedOutput = [ + [ + { content: 'Italic', type: 'em' }, + { content: 'and', type: 'normal' }, + { content: 'bold', type: 'strong' }, + { content: 'formatting', type: 'normal' }, + ], + ]; + + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToHTML - Basic test', () => { + const input = `This is regular text +Here is a new line +There is some words **with a bold** section +Here is a line *with an italic* section`; + + const expectedOutput = `

This is regular text
Here is a new line
There is some words with a bold section
Here is a line with an italic section

`; + + const output = markdownToHTML(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToHTML - Empty input', () => { + const input = ''; + const expectedOutput = ''; + const output = markdownToHTML(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToHTML - No formatting', () => { + const input = `This is a simple test +with no formatting`; + + const expectedOutput = `

This is a simple test
with no formatting

`; + const output = markdownToHTML(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToHTML - Only bold formatting', () => { + const input = `This is a **bold** test`; + + const expectedOutput = `

This is a bold test

`; + const output = markdownToHTML(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToHTML - Only italic formatting', () => { + const input = `This is an *italic* test`; + + const expectedOutput = `

This is an italic test

`; + const output = markdownToHTML(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToHTML - Mixed formatting', () => { + const input = `*Italic* and **bold** formatting`; + + const expectedOutput = `

Italic and bold formatting

`; + const output = markdownToHTML(input); + expect(output).toEqual(expectedOutput); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef69a1706..948a6d5ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -178,6 +178,9 @@ importers: '@braintree/sanitize-url': specifier: ^6.0.0 version: 6.0.0 + '@khanacademy/simple-markdown': + specifier: ^0.8.6 + version: 0.8.6_wcqkhtmu7mswc6yz4uyexck3ty cytoscape: specifier: ^3.23.0 version: 3.23.0 @@ -322,7 +325,7 @@ importers: version: 1.0.0 vitepress: specifier: ^1.0.0-alpha.46 - version: 1.0.0-alpha.46_tbpndr44ulefs3hehwpi2mkf2y + version: 1.0.0-alpha.46_hoyvfk3ab7nzsjkhptt6ai7rzq vitepress-plugin-search: specifier: ^1.0.4-alpha.19 version: 1.0.4-alpha.19_g67lr3vgasogkevpbew55lljzq @@ -1705,10 +1708,10 @@ packages: resolution: {integrity: sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg==} dev: true - /@docsearch/js/3.3.3_tbpndr44ulefs3hehwpi2mkf2y: + /@docsearch/js/3.3.3_hoyvfk3ab7nzsjkhptt6ai7rzq: resolution: {integrity: sha512-2xAv2GFuHzzmG0SSZgf8wHX0qZX8n9Y1ZirKUk5Wrdc+vH9CL837x2hZIUdwcPZI9caBA+/CzxsS68O4waYjUQ==} dependencies: - '@docsearch/react': 3.3.3_tbpndr44ulefs3hehwpi2mkf2y + '@docsearch/react': 3.3.3_hoyvfk3ab7nzsjkhptt6ai7rzq preact: 10.11.0 transitivePeerDependencies: - '@algolia/client-search' @@ -1717,7 +1720,7 @@ packages: - react-dom dev: true - /@docsearch/react/3.3.3_tbpndr44ulefs3hehwpi2mkf2y: + /@docsearch/react/3.3.3_hoyvfk3ab7nzsjkhptt6ai7rzq: resolution: {integrity: sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' @@ -1735,6 +1738,8 @@ packages: '@algolia/autocomplete-preset-algolia': 1.7.4_qs6lk5nhygj2o3hj4sf6xnr724 '@docsearch/css': 3.3.3 algoliasearch: 4.14.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 transitivePeerDependencies: - '@algolia/client-search' dev: true @@ -2464,6 +2469,17 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@khanacademy/simple-markdown/0.8.6_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-mAUlR9lchzfqunR89pFvNI51jQKsMpJeWYsYWw0DQcUXczn/T/V6510utgvm7X0N3zN87j1SvuKk8cMbl9IAFw==} + peerDependencies: + react: 16.14.0 + react-dom: 16.14.0 + dependencies: + '@types/react': 18.0.28 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + dev: false + /@leichtgewicht/ip-codec/2.0.4: resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} dev: true @@ -3047,6 +3063,10 @@ packages: resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} dev: true + /@types/prop-types/15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: false + /@types/qs/6.9.7: resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} dev: true @@ -3055,6 +3075,14 @@ packages: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} dev: true + /@types/react/18.0.28: + resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.1 + dev: false + /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: @@ -3072,6 +3100,10 @@ packages: rollup: 2.79.1 dev: true + /@types/scheduler/0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + dev: false + /@types/semver/7.3.12: resolution: {integrity: sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==} dev: true @@ -5248,6 +5280,10 @@ packages: resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} dev: true + /csstype/3.1.1: + resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + dev: false + /cypress-image-snapshot/4.0.1_cypress@12.5.1+jest@29.3.1: resolution: {integrity: sha512-PBpnhX/XItlx3/DAk5ozsXQHUi72exybBNH5Mpqj1DVmjq+S5Jd9WE5CRa4q5q0zuMZb2V2VpXHth6MjFpgj9Q==} engines: {node: '>=8'} @@ -8213,7 +8249,6 @@ packages: /js-tokens/4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true /js-yaml/3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -8621,6 +8656,12 @@ packages: resolution: {integrity: sha512-cHlYSUpL2s7Fb3394mYxwTYj8niTaNHUCLr0qdiCXQfSjfuA7CKofpX2uSwEfFDQ0EB7JcnMnm+GjbqqoinYYg==} dev: true + /loose-envify/1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + /loupe/2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: @@ -9432,7 +9473,6 @@ packages: /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: true /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} @@ -9943,6 +9983,13 @@ packages: sisteransi: 1.0.5 dev: true + /prop-types/15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + /proxy-addr/2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -10055,6 +10102,20 @@ packages: unpipe: 1.0.0 dev: true + /react-dom/16.14.0_react@16.14.0: + resolution: {integrity: sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==} + peerDependencies: + react: ^16.14.0 + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + prop-types: 15.8.1 + react: 16.14.0 + scheduler: 0.19.1 + + /react-is/16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + /react-is/17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true @@ -10063,6 +10124,14 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true + /react/16.14.0: + resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + prop-types: 15.8.1 + /read-pkg-up/7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -10471,6 +10540,12 @@ packages: xmlchars: 2.2.0 dev: true + /scheduler/0.19.1: + resolution: {integrity: sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + /schema-utils/3.1.1: resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==} engines: {node: '>= 10.13.0'} @@ -11769,16 +11844,16 @@ packages: '@types/markdown-it': 12.2.3 flexsearch: 0.7.31 markdown-it: 13.0.1 - vitepress: 1.0.0-alpha.46_tbpndr44ulefs3hehwpi2mkf2y + vitepress: 1.0.0-alpha.46_hoyvfk3ab7nzsjkhptt6ai7rzq vue: 3.2.45 dev: true - /vitepress/1.0.0-alpha.46_tbpndr44ulefs3hehwpi2mkf2y: + /vitepress/1.0.0-alpha.46_hoyvfk3ab7nzsjkhptt6ai7rzq: resolution: {integrity: sha512-HiKiHzC0iTPsRsKs8XcsMeMzCpcCt5LWcX9mpDr288Ju+nQf1G8A2+Wm44ZkBsVv4EHxFK4ChmWyZrL1OJUXpg==} hasBin: true dependencies: '@docsearch/css': 3.3.3 - '@docsearch/js': 3.3.3_tbpndr44ulefs3hehwpi2mkf2y + '@docsearch/js': 3.3.3_hoyvfk3ab7nzsjkhptt6ai7rzq '@vitejs/plugin-vue': 4.0.0_vite@4.1.1+vue@3.2.45 '@vue/devtools-api': 6.5.0 '@vueuse/core': 9.12.0_vue@3.2.45 From fd9ad95346094315b25faae5f7a210282959e027 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Wed, 22 Mar 2023 18:41:31 +0100 Subject: [PATCH 03/11] #4220 Handling paragraphs and html labels with classes in mindmaps. --- cypress/platform/knsv2.html | 19 ++++---- .../mermaid/src/diagrams/mindmap/styles.js | 7 +++ .../mermaid/src/diagrams/mindmap/svgDraw.js | 21 +++------ .../mermaid/src/rendering-util/createText.js | 35 +++++++++------ .../rendering-util/handle-markdown-text.js | 18 +++++++- .../handle-markdown-text.spec.js | 45 ++++++++++++++++++- 6 files changed, 106 insertions(+), 39 deletions(-) diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 923529074..4335beaee 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -66,21 +66,24 @@ h --3i -->a b --> d(The dog in the hog) c --> d
-
+    
 mindmap
-    id1["`Start`"]
+    id1["`**Start2**
+    second line 😎 with long text that is wrapping to the next line`"]
       id2["`Child **with bold** text`"]
       id3["`Children of which some
       is using *italic type of* text`"]
       id4[Child]
+      id5["`Child
+      Row
+      and another
+      `"]
     
-
+    
 mindmap
-    id1["`Start
-second line 😎`"]
-      id2[Child]
-      id3[Child]
-      id4[Child]
+    id1["`**Start** with
+
+    a second line 😎`"]
     
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
diff --git a/packages/mermaid/src/diagrams/mindmap/styles.js b/packages/mermaid/src/diagrams/mindmap/styles.js
index 986a04514..863522fdf 100644
--- a/packages/mermaid/src/diagrams/mindmap/styles.js
+++ b/packages/mermaid/src/diagrams/mindmap/styles.js
@@ -70,5 +70,12 @@ const getStyles = (options) =>
   .edge {
     fill: none;
   }
+  .mindmap-node-label {
+    dy: 1em;
+    alignment-baseline: middle;
+    text-anchor: middle;
+    dominant-baseline: middle;
+    text-align: center;
+  }
 `;
 export default getStyles;
diff --git a/packages/mermaid/src/diagrams/mindmap/svgDraw.js b/packages/mermaid/src/diagrams/mindmap/svgDraw.js
index 5394e004e..8b58c11a3 100644
--- a/packages/mermaid/src/diagrams/mindmap/svgDraw.js
+++ b/packages/mermaid/src/diagrams/mindmap/svgDraw.js
@@ -204,7 +204,7 @@ const roundedRectBkg = function (elem, node) {
  * @returns {number} The height nodes dom element
  */
 export const drawNode = function (elem, node, fullSection, conf) {
-  const htmlLabels = false;
+  const htmlLabels = conf.htmlLabels;
   const section = fullSection % (MAX_SECTIONS - 1);
   const nodeElem = elem.append('g');
   node.section = section;
@@ -217,19 +217,12 @@ export const drawNode = function (elem, node, fullSection, conf) {
 
   // Create the wrapped text element
   const textElem = nodeElem.append('g');
-  const newEl = createText(textElem, node.descr, { useHtmlLabels: htmlLabels, width: node.width });
-  // const txt = textElem.node().appendChild(newEl);
-  // const txt = textElem.append(newEl);
-  // const txt = textElem
-  //   .append('text')
-  //   .text(node.descr)
-  //   .attr('dy', '1em')
-  //   .attr('alignment-baseline', 'middle')
-  //   .attr('dominant-baseline', 'middle')
-  //   .attr('text-anchor', 'middle')
-  //   .call(wrap, node.width);
-  // const newerEl = textElem.node().appendChild(newEl);
-  // setSize(textElem);
+  const newEl = createText(textElem, node.descr, {
+    useHtmlLabels: htmlLabels,
+    width: node.width,
+    classes: 'mindmap-node-label',
+  });
+
   if (!htmlLabels) {
     textElem
       .attr('dy', '1em')
diff --git a/packages/mermaid/src/rendering-util/createText.js b/packages/mermaid/src/rendering-util/createText.js
index 47e0776e4..5eea6f4c8 100644
--- a/packages/mermaid/src/rendering-util/createText.js
+++ b/packages/mermaid/src/rendering-util/createText.js
@@ -17,19 +17,22 @@ function applyStyle(dom, styleFn) {
 /**
  * @param element
  * @param {any} node
+ * @param width
+ * @param classes
  * @returns {SVGForeignObjectElement} Node
  */
-function addHtmlSpan(element, node) {
+function addHtmlSpan(element, node, width, classes) {
   const fo = element.append('foreignObject');
-  const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
+  // const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
+  // const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
   const div = fo.append('xhtml:div');
+  // const div = body.append('div');
+  // const div = fo.append('div');
 
   const label = node.label;
   const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
   div.html(
-    '' +
       label +
@@ -37,16 +40,22 @@ function addHtmlSpan(element, node) {
   );
 
   applyStyle(div, node.labelStyle);
-  div.style('display', 'inline-block');
-  const bbox = div.node().getBoundingClientRect();
+  div.style('display', 'table-cell');
+  div.style('white-space', 'nowrap');
+  div.style('max-width', width + 'px');
+  div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
+
+  let bbox = div.node().getBoundingClientRect();
+  if (bbox.width === width) {
+    div.style('display', 'table');
+    div.style('white-space', 'break-spaces');
+    div.style('width', '200px');
+    bbox = div.node().getBoundingClientRect();
+  }
+
   fo.style('width', bbox.width);
   fo.style('height', bbox.height);
 
-  const divNode = div.node();
-  window.divNode = divNode;
-  // Fix for firefox
-  div.style('white-space', 'nowrap');
-  div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
   return fo.node();
 }
 
@@ -158,7 +167,7 @@ export const createText = (
       ),
       labelStyle: style.replace('fill:', 'color:'),
     };
-    let vertexNode = addHtmlSpan(el, node);
+    let vertexNode = addHtmlSpan(el, node, width, classes);
     return vertexNode;
   } else {
     const structuredText = markdownToLines(text);
diff --git a/packages/mermaid/src/rendering-util/handle-markdown-text.js b/packages/mermaid/src/rendering-util/handle-markdown-text.js
index c4d10d9dc..cd79623fe 100644
--- a/packages/mermaid/src/rendering-util/handle-markdown-text.js
+++ b/packages/mermaid/src/rendering-util/handle-markdown-text.js
@@ -1,12 +1,25 @@
 import SimpleMarkdown from '@khanacademy/simple-markdown';
 
+/**
+ *
+ * @param markdown
+ */
+function preprocessMarkdown(markdown) {
+  // Replace multiple newlines with a single newline
+  const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
+  // Remove extra spaces at the beginning of each line
+  const withoutExtraSpaces = withoutMultipleNewlines.replace(/^\s+/gm, '');
+  return withoutExtraSpaces;
+}
+
 /**
  *
  * @param markdown
  */
 export function markdownToLines(markdown) {
+  const preprocessedMarkdown = preprocessMarkdown(markdown);
   const mdParse = SimpleMarkdown.defaultBlockParse;
-  const syntaxTree = mdParse(markdown);
+  const syntaxTree = mdParse(preprocessedMarkdown);
 
   let lines = [[]];
   let currentLine = 0;
@@ -19,6 +32,7 @@ export function markdownToLines(markdown) {
   function processNode(node, parentType) {
     if (node.type === 'text') {
       const textLines = node.content.split('\n');
+
       textLines.forEach((textLine, index) => {
         if (index !== 0) {
           currentLine++;
@@ -62,7 +76,7 @@ export function markdownToHTML(markdown) {
    */
   function output(node) {
     if (node.type === 'text') {
-      return node.content.replace(/\n/g, '
'); + return node.content.replace(/\n/g, '
'); } else if (node.type === 'strong') { return `${node.content.map(output).join('')}`; } else if (node.type === 'em') { diff --git a/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js b/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js index db542543a..005dd42f6 100644 --- a/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js +++ b/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js @@ -95,6 +95,47 @@ test('markdownToLines - Only bold formatting', () => { expect(output).toEqual(expectedOutput); }); +test('markdownToLines - paragraph 1', () => { + const input = `**Start** with + a second line`; + + const expectedOutput = [ + [ + { content: 'Start', type: 'strong' }, + { content: 'with', type: 'normal' }, + ], + [ + { content: 'a', type: 'normal' }, + { content: 'second', type: 'normal' }, + { content: 'line', type: 'normal' }, + ], + ]; + + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + +test('markdownToLines - paragraph', () => { + const input = `**Start** with + + a second line`; + + const expectedOutput = [ + [ + { content: 'Start', type: 'strong' }, + { content: 'with', type: 'normal' }, + ], + [ + { content: 'a', type: 'normal' }, + { content: 'second', type: 'normal' }, + { content: 'line', type: 'normal' }, + ], + ]; + + const output = markdownToLines(input); + expect(output).toEqual(expectedOutput); +}); + test('markdownToLines - Only italic formatting', () => { const input = `This is an *italic* test`; @@ -134,7 +175,7 @@ Here is a new line There is some words **with a bold** section Here is a line *with an italic* section`; - const expectedOutput = `

This is regular text
Here is a new line
There is some words with a bold section
Here is a line with an italic section

`; + const expectedOutput = `

This is regular text
Here is a new line
There is some words with a bold section
Here is a line with an italic section

`; const output = markdownToHTML(input); expect(output).toEqual(expectedOutput); @@ -151,7 +192,7 @@ test('markdownToHTML - No formatting', () => { const input = `This is a simple test with no formatting`; - const expectedOutput = `

This is a simple test
with no formatting

`; + const expectedOutput = `

This is a simple test
with no formatting

`; const output = markdownToHTML(input); expect(output).toEqual(expectedOutput); }); From fbeb0163988bc6b7732f9a99b5406371a5b263b0 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Mon, 27 Mar 2023 14:43:59 +0200 Subject: [PATCH 04/11] test commit --- cypress/platform/knsv2.html | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 4335beaee..61ff556e8 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -56,6 +56,7 @@
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
+%%
 graph BT
 a{The cat in the hat} -- 1o --> b
 a -- 2o --> c

From 63160293c72bc7ca9cce8af4bf0add625ddf049b Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Tue, 28 Mar 2023 15:28:52 +0200
Subject: [PATCH 05/11] Updating support for the new type of strings for
 flowcharts-v2

---
 cypress/platform/knsv2.html                   | 53 ++++++++++++++--
 packages/mermaid/src/Diagram.ts               | 10 +++
 .../mermaid/src/dagre-wrapper/clusters.js     | 17 +++--
 packages/mermaid/src/dagre-wrapper/edges.js   | 10 ++-
 packages/mermaid/src/dagre-wrapper/nodes.js   | 11 ++--
 .../mermaid/src/dagre-wrapper/shapes/util.js  | 22 +++++--
 .../mermaid/src/diagrams/flowchart/flowDb.js  | 42 ++++++++-----
 .../src/diagrams/flowchart/flowRenderer-v2.js | 62 ++++++++++++-------
 .../src/diagrams/flowchart/parser/flow.jison  | 12 +++-
 .../mermaid/src/diagrams/flowchart/styles.ts  |  9 +++
 .../mermaid/src/rendering-util/createText.js  | 52 +++++++++++++---
 .../handle-markdown-text.spec.js              | 31 ++++++++++
 12 files changed, 256 insertions(+), 75 deletions(-)

diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html
index 61ff556e8..69eab8b62 100644
--- a/cypress/platform/knsv2.html
+++ b/cypress/platform/knsv2.html
@@ -51,6 +51,9 @@
         font-family: monospace;
         font-size: 72px;
       }
+      /* tspan {
+        font-size: 6px !important;
+      } */
     
   
   
@@ -58,7 +61,7 @@
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 %%
 graph BT
-a{The cat in the hat} -- 1o --> b
+a("`The **cat** in the hat} -- 1o --> b
 a -- 2o --> c
 a -- 3o --> d
 g --2i--> a
@@ -66,8 +69,47 @@ d --1i--> a
 h --3i -->a
 b --> d(The dog in the hog)
 c --> d
+%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
     
-
+    
+flowchart LR
+b("`The dog in **the** hog... a a a a *very long text* about it
+Word!
+
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `")
+
+
+      %%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart LR
+b("`The dog in **the** hog... a a a a *very long text* about it
+Word!
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `")
+
+
+      %%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart LR
+b("The dog in the hog... a very
long text about it
Word!") +
+
+      %%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart LR
+b("The dog in the hog... a very
long text about it
Word!") +
+
+flowchart LR
+subgraph "One"
+  a("`The **cat**
+  in the hat`") -- 1o --> b{{"`The **dog** in the hog`"}}
+end
+subgraph "`**Two**`"
+  c("`The **cat**
+  in the hat`") -- "`1o **ipa**`" --> d("The dog in the hog")
+end
+
+
 mindmap
     id1["`**Start2**
     second line 😎 with long text that is wrapping to the next line`"]
@@ -83,8 +125,9 @@ mindmap
     
 mindmap
     id1["`**Start** with
-
     a second line 😎`"]
+      id2["`The dog in **the** hog... a *very long text* about it
+Word!`"]
     
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
@@ -290,9 +333,9 @@ mindmap
           // defaultRenderer: 'elk',
           useMaxWidth: false,
           // htmlLabels: false,
-          htmlLabels: true,
+          htmlLabels: false,
         },
-        htmlLabels: true,
+        htmlLabels: false,
         gantt: {
           useMaxWidth: false,
         },
diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts
index 3010ce130..3943a84c2 100644
--- a/packages/mermaid/src/Diagram.ts
+++ b/packages/mermaid/src/Diagram.ts
@@ -44,6 +44,16 @@ export class Diagram {
     // calls diagram.db.clear(), which would reset anything set by
     // extractFrontMatter().
     this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db));
+
+    // this.parser.parse = (text: string) => {
+    //   console.log('parse called');
+    //   try {
+    //     originalParse(extractFrontMatter(text, this.db));
+    //   } catch (e) {
+    //     console.log('parse called', e);
+    //   }
+    // };
+
     this.parser.parser.yy = this.db;
     if (diagram.init) {
       diagram.init(cnf);
diff --git a/packages/mermaid/src/dagre-wrapper/clusters.js b/packages/mermaid/src/dagre-wrapper/clusters.js
index 57c3ff513..453fcb8f5 100644
--- a/packages/mermaid/src/dagre-wrapper/clusters.js
+++ b/packages/mermaid/src/dagre-wrapper/clusters.js
@@ -1,12 +1,13 @@
 import intersectRect from './intersect/intersect-rect';
 import { log } from '../logger';
 import createLabel from './createLabel';
+import { createText } from '../rendering-util/createText';
 import { select } from 'd3';
 import { getConfig } from '../config';
 import { evaluate } from '../diagrams/common/common';
 
 const rect = (parent, node) => {
-  log.trace('Creating subgraph rect for ', node.id, node);
+  log.info('Creating subgraph rect for ', node.id, node);
 
   // Add outer g element
   const shapeSvg = parent
@@ -17,12 +18,18 @@ const rect = (parent, node) => {
   // add the rect
   const rect = shapeSvg.insert('rect', ':first-child');
 
+  const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
+
   // Create the label and insert it after the rect
   const label = shapeSvg.insert('g').attr('class', 'cluster-label');
 
-  const text = label
-    .node()
-    .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
+  // const text = label
+  //   .node()
+  //   .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
+  const text =
+    node.labelType === 'markdown'
+      ? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels })
+      : label.node().appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
 
   // Get the size of the label
   let bbox = text.getBBox();
@@ -61,7 +68,7 @@ const rect = (parent, node) => {
     'transform',
     // This puts the labal on top of the box instead of inside it
     // 'translate(' + (node.x - bbox.width / 2) + ', ' + (node.y - node.height / 2 - bbox.height) + ')'
-    'translate(' + (node.x - bbox.width / 2) + ', ' + (node.y - node.height / 2) + ')'
+    'translate(' + node.x + ', ' + (node.y - node.height / 2) + ')'
   );
 
   const rectBox = rect.node().getBBox();
diff --git a/packages/mermaid/src/dagre-wrapper/edges.js b/packages/mermaid/src/dagre-wrapper/edges.js
index f8c113694..4f83e938a 100644
--- a/packages/mermaid/src/dagre-wrapper/edges.js
+++ b/packages/mermaid/src/dagre-wrapper/edges.js
@@ -1,5 +1,6 @@
 import { log } from '../logger';
 import createLabel from './createLabel';
+import { createText } from '../rendering-util/createText';
 import { line, curveBasis, select } from 'd3';
 import { getConfig } from '../config';
 import utils from '../utils';
@@ -14,8 +15,13 @@ export const clear = () => {
 };
 
 export const insertEdgeLabel = (elem, edge) => {
+  const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
   // Create the actual text element
-  const labelElement = createLabel(edge.label, edge.labelStyle);
+  const labelElement =
+    edge.labelType === 'markdown'
+      ? createText(elem, edge.label, { style: edge.labelStyle, useHtmlLabels })
+      : createLabel(edge.label, edge.labelStyle);
+  log.info('abc82', edge, edge.labelType);
 
   // Create outer g, edgeLabel, this will be positioned after graph layout
   const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
@@ -26,7 +32,7 @@ export const insertEdgeLabel = (elem, edge) => {
 
   // Center the label
   let bbox = labelElement.getBBox();
-  if (evaluate(getConfig().flowchart.htmlLabels)) {
+  if (useHtmlLabels) {
     const div = labelElement.children[0];
     const dv = select(labelElement);
     bbox = div.getBoundingClientRect();
diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js
index 49b96b685..3e71f500a 100644
--- a/packages/mermaid/src/dagre-wrapper/nodes.js
+++ b/packages/mermaid/src/dagre-wrapper/nodes.js
@@ -313,19 +313,18 @@ const cylinder = (parent, node) => {
 const rect = (parent, node) => {
   const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true);
 
-  log.trace('Classes = ', node.classes);
   // add the rect
   const rect = shapeSvg.insert('rect', ':first-child');
 
-  const totalWidth = bbox.width + node.padding;
-  const totalHeight = bbox.height + node.padding;
+  const totalWidth = bbox.width + node.padding * 2;
+  const totalHeight = bbox.height + node.padding * 2;
   rect
     .attr('class', 'basic label-container')
     .attr('style', node.style)
     .attr('rx', node.rx)
     .attr('ry', node.ry)
-    .attr('x', -bbox.width / 2 - halfPadding)
-    .attr('y', -bbox.height / 2 - halfPadding)
+    .attr('x', -bbox.width / 2 - node.padding)
+    .attr('y', -bbox.height / 2 - node.padding)
     .attr('width', totalWidth)
     .attr('height', totalHeight);
 
@@ -352,7 +351,7 @@ const rect = (parent, node) => {
 const labelRect = (parent, node) => {
   const { shapeSvg } = labelHelper(parent, node, 'label', true);
 
-  log.trace('Classes = ', node.classes);
+  log.info('Classes = ', node.classes);
   // add the rect
   const rect = shapeSvg.insert('rect', ':first-child');
 
diff --git a/packages/mermaid/src/dagre-wrapper/shapes/util.js b/packages/mermaid/src/dagre-wrapper/shapes/util.js
index 6de0da638..d362d08f6 100644
--- a/packages/mermaid/src/dagre-wrapper/shapes/util.js
+++ b/packages/mermaid/src/dagre-wrapper/shapes/util.js
@@ -1,4 +1,5 @@
 import createLabel from '../createLabel';
+import { createText } from '../../rendering-util/createText';
 import { getConfig } from '../../config';
 import { decodeEntities } from '../../mermaidAPI';
 import { select } from 'd3';
@@ -27,9 +28,17 @@ export const labelHelper = (parent, node, _classes, isNode) => {
     labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];
   }
 
-  const text = label
-    .node()
-    .appendChild(
+  const textNode = label.node();
+  let text;
+  if (node.labelType === 'markdown') {
+    // text = textNode;
+    text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
+      useHtmlLabels: getConfig().flowchart.htmlLabels,
+      width: node.width || 200,
+      classes: 'markdown-node-label',
+    });
+  } else {
+    text = textNode.appendChild(
       createLabel(
         sanitizeText(decodeEntities(labelText), getConfig()),
         node.labelStyle,
@@ -37,6 +46,7 @@ export const labelHelper = (parent, node, _classes, isNode) => {
         isNode
       )
     );
+  }
 
   // Get the size of the label
   let bbox = text.getBBox();
@@ -52,7 +62,11 @@ export const labelHelper = (parent, node, _classes, isNode) => {
   const halfPadding = node.padding / 2;
 
   // Center the label
-  label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
+  if (getConfig().flowchart.htmlLabels) {
+    label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
+  } else {
+    label.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')');
+  }
 
   return { shapeSvg, bbox, halfPadding, label };
 };
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js
index 31196ba96..26bc75c14 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDb.js
+++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js
@@ -59,13 +59,14 @@ export const lookUpDomId = function (id) {
  *
  * @param _id
  * @param text
+ * @param textObj
  * @param type
  * @param style
  * @param classes
  * @param dir
  * @param props
  */
-export const addVertex = function (_id, text, type, style, classes, dir, props = {}) {
+export const addVertex = function (_id, textObj, type, style, classes, dir, props = {}) {
   let txt;
   let id = _id;
   if (id === undefined) {
@@ -80,16 +81,17 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
   if (vertices[id] === undefined) {
     vertices[id] = {
       id: id,
+      labelType: 'text',
       domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
       styles: [],
       classes: [],
     };
   }
   vertexCounter++;
-  if (text !== undefined) {
+  if (textObj !== undefined) {
     config = configApi.getConfig();
-    txt = sanitizeText(text.trim());
-
+    txt = sanitizeText(textObj.text.trim());
+    vertices[id].labelType = textObj.type;
     // strip quotes if string starts and ends with a quote
     if (txt[0] === '"' && txt[txt.length - 1] === '"') {
       txt = txt.substring(1, txt.length - 1);
@@ -131,24 +133,26 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
  * @param _end
  * @param type
  * @param linkText
+ * @param linkTextObj
  */
-export const addSingleLink = function (_start, _end, type, linkText) {
+export const addSingleLink = function (_start, _end, type) {
   let start = _start;
   let end = _end;
   // if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
   // if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end;
   // log.info('Got edge...', start, end);
 
-  const edge = { start: start, end: end, type: undefined, text: '' };
-  linkText = type.text;
+  const edge = { start: start, end: end, type: undefined, text: '', labelType: 'text' };
+  const linkTextObj = type.text;
 
-  if (linkText !== undefined) {
-    edge.text = sanitizeText(linkText.trim());
+  if (linkTextObj !== undefined) {
+    edge.text = sanitizeText(linkTextObj.text.trim());
 
     // strip quotes if string starts and ends with a quote
     if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
       edge.text = edge.text.substring(1, edge.text.length - 1);
     }
+    edge.labelType = linkTextObj.type;
   }
 
   if (type !== undefined) {
@@ -158,11 +162,11 @@ export const addSingleLink = function (_start, _end, type, linkText) {
   }
   edges.push(edge);
 };
-export const addLink = function (_start, _end, type, linktext) {
+export const addLink = function (_start, _end, type) {
   let i, j;
   for (i = 0; i < _start.length; i++) {
     for (j = 0; j < _end.length; j++) {
-      addSingleLink(_start[i], _end[j], type, linktext);
+      addSingleLink(_start[i], _end[j], type);
     }
   }
 };
@@ -457,10 +461,9 @@ export const defaultStyle = function () {
  * @param _title
  */
 export const addSubGraph = function (_id, list, _title) {
-  // console.log('addSubGraph', _id, list, _title);
-  let id = _id.trim();
-  let title = _title;
-  if (_id === _title && _title.match(/\s/)) {
+  let id = _id.text.trim();
+  let title = _title.text;
+  if (_id === _title && _title.text.match(/\s/)) {
     id = undefined;
   }
   /** @param a */
@@ -502,7 +505,14 @@ export const addSubGraph = function (_id, list, _title) {
   title = title || '';
   title = sanitizeText(title);
   subCount = subCount + 1;
-  const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [], dir };
+  const subGraph = {
+    id: id,
+    nodes: nodeList,
+    title: title.trim(),
+    classes: [],
+    dir,
+    labelType: _title.type,
+  };
 
   log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
 
diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
index 2d3e21a44..a87f23e04 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
+++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
@@ -47,7 +47,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
     if (vertex.classes.length > 0) {
       classStr = vertex.classes.join(' ');
     }
-
+    classStr = classStr + ' flowchart-label';
     const styles = getStylesFromArray(vertex.styles);
 
     // Use vertex id as text in the box if no text is provided by the graph definition
@@ -55,31 +55,36 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
 
     // We create a SVG label, either by delegating to addHtmlLabel or manually
     let vertexNode;
-    if (evaluate(getConfig().flowchart.htmlLabels)) {
-      // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
-      const node = {
-        label: vertexText.replace(
-          /fa[blrs]?:fa-[\w-]+/g,
-          (s) => ``
-        ),
-      };
-      vertexNode = addHtmlLabel(svg, node).node();
-      vertexNode.parentNode.removeChild(vertexNode);
+    log.info('vertex', vertex, vertex.labelType);
+    if (vertex.labelType === 'markdown') {
+      log.info('vertex', vertex, vertex.labelType);
     } else {
-      const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
-      svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
+      if (evaluate(getConfig().flowchart.htmlLabels)) {
+        // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
+        const node = {
+          label: vertexText.replace(
+            /fa[blrs]?:fa-[\w-]+/g,
+            (s) => ``
+          ),
+        };
+        vertexNode = addHtmlLabel(svg, node).node();
+        vertexNode.parentNode.removeChild(vertexNode);
+      } else {
+        const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
+        svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
 
-      const rows = vertexText.split(common.lineBreakRegex);
+        const rows = vertexText.split(common.lineBreakRegex);
 
-      for (const row of rows) {
-        const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
-        tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
-        tspan.setAttribute('dy', '1em');
-        tspan.setAttribute('x', '1');
-        tspan.textContent = row;
-        svgLabel.appendChild(tspan);
+        for (const row of rows) {
+          const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
+          tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
+          tspan.setAttribute('dy', '1em');
+          tspan.setAttribute('x', '1');
+          tspan.textContent = row;
+          svgLabel.appendChild(tspan);
+        }
+        vertexNode = svgLabel;
       }
-      vertexNode = svgLabel;
     }
 
     let radious = 0;
@@ -146,6 +151,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
       labelStyle: styles.labelStyle,
       shape: _shape,
       labelText: vertexText,
+      labelType: vertex.labelType,
       rx: radious,
       ry: radious,
       class: classStr,
@@ -165,6 +171,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
 
     log.info('setNode', {
       labelStyle: styles.labelStyle,
+      labelType: vertex.labelType,
       shape: _shape,
       labelText: vertexText,
       rx: radious,
@@ -312,7 +319,7 @@ export const addEdges = function (edges, g, diagObj) {
       edgeData.labelpos = 'c';
     }
 
-    edgeData.labelType = 'text';
+    edgeData.labelType = edge.labelType;
     edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
 
     if (edge.style === undefined) {
@@ -405,7 +412,14 @@ export const draw = function (text, id, _version, diagObj) {
   for (let i = subGraphs.length - 1; i >= 0; i--) {
     subG = subGraphs[i];
     log.info('Subgraph - ', subG);
-    diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
+    diagObj.db.addVertex(
+      subG.id,
+      { text: subG.title, type: subG.labelType },
+      'group',
+      undefined,
+      subG.classes,
+      subG.dir
+    );
   }
 
   // Fetch the vertices/nodes and edges/links from the parsed graph definition
diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
index e2dad5881..d21104d49 100644
--- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
+++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
@@ -7,6 +7,7 @@
 /* lexical grammar */
 %lex
 %x string
+%x md_string
 %x acc_title
 %x acc_descr
 %x acc_descr_multiline
@@ -37,6 +38,9 @@ accDescr\s*"{"\s*                                { this.begin("acc_descr_multili
 [\}]                       { this.popState(); }
 [^\}]*                     return "acc_descr_multiline_value";
 // .*[^\n]*                    {  return "acc_descr_line"}
+["][`]          { this.begin("md_string");}
+[^`"]+        { return "MD_STR";}
+[`]["]          { this.popState();}
 ["]                     this.begin("string");
 ["]             this.popState();
 [^"]*           return "STR";
@@ -434,11 +438,13 @@ arrowText:
     ;
 
 text: textToken
-    {$$=$1;}
+    {$$={text:$1, type: 'text'};}
     | text textToken
-    {$$=$1+''+$2;}
+    {$$={text:$1.text+''+$2, type: 'text'};}
     | STR
-    {$$=$1;}
+    {$$={text: $1, type: 'text'};}
+    | MD_STR
+    {$$={text: $1, type: 'markdown'};}
     ;
 
 
diff --git a/packages/mermaid/src/diagrams/flowchart/styles.ts b/packages/mermaid/src/diagrams/flowchart/styles.ts
index a89d33d3d..86ffcb7ed 100644
--- a/packages/mermaid/src/diagrams/flowchart/styles.ts
+++ b/packages/mermaid/src/diagrams/flowchart/styles.ts
@@ -41,6 +41,15 @@ const getStyles = (options: FlowChartStyleOptions) =>
     stroke: ${options.nodeBorder};
     stroke-width: 1px;
   }
+  .flowchart-label text {
+    text-anchor: middle;
+  }
+  // .flowchart-label .text-outer-tspan {
+  //   text-anchor: middle;
+  // }
+  // .flowchart-label .text-inner-tspan {
+  //   text-anchor: start;
+  // }
 
   .node .label {
     text-align: center;
diff --git a/packages/mermaid/src/rendering-util/createText.js b/packages/mermaid/src/rendering-util/createText.js
index 5eea6f4c8..0fc7dda54 100644
--- a/packages/mermaid/src/rendering-util/createText.js
+++ b/packages/mermaid/src/rendering-util/createText.js
@@ -49,7 +49,7 @@ function addHtmlSpan(element, node, width, classes) {
   if (bbox.width === width) {
     div.style('display', 'table');
     div.style('white-space', 'break-spaces');
-    div.style('width', '200px');
+    div.style('width', width + 'px');
     bbox = div.node().getBoundingClientRect();
   }
 
@@ -70,8 +70,9 @@ function addHtmlSpan(element, node, width, classes) {
 function createTspan(textElement, lineIndex, lineHeight) {
   return textElement
     .append('tspan')
+    .attr('class', 'text-outer-tspan')
     .attr('x', 0)
-    .attr('y', lineIndex * lineHeight + 'em')
+    .attr('y', lineIndex * lineHeight - 0.1 + 'em')
     .attr('dy', lineHeight + 'em');
 }
 
@@ -86,9 +87,13 @@ function createTspan(textElement, lineIndex, lineHeight) {
 function createFormattedText(width, g, structuredText) {
   const lineHeight = 1.1;
 
-  const textElement = g.append('text');
-
-  structuredText.forEach((line, lineIndex) => {
+  const textElement = g.append('text').attr('y', '-10.1');
+  // .attr('dominant-baseline', 'middle')
+  // .attr('text-anchor', 'middle');
+  // .attr('text-anchor', 'middle');
+  let lineIndex = -1;
+  structuredText.forEach((line) => {
+    lineIndex++;
     let tspan = createTspan(textElement, lineIndex, lineHeight);
 
     let words = [...line].reverse();
@@ -108,10 +113,13 @@ function createFormattedText(width, g, structuredText) {
         updateTextContentAndStyles(tspan, wrappedLine);
 
         wrappedLine = [];
-        tspan = createTspan(textElement, ++lineIndex, lineHeight);
+        lineIndex++;
+        tspan = createTspan(textElement, lineIndex, lineHeight);
       }
     }
   });
+  return textElement.node();
+  // return g.node();
 }
 
 /**
@@ -124,12 +132,36 @@ function createFormattedText(width, g, structuredText) {
 function updateTextContentAndStyles(tspan, wrappedLine) {
   tspan.text('');
 
-  wrappedLine.forEach((word) => {
-    tspan
+  wrappedLine.forEach((word, index) => {
+    const innerTspan = tspan
       .append('tspan')
       .attr('font-style', word.type === 'em' ? 'italic' : 'normal')
-      .attr('font-weight', word.type === 'strong' ? 'bold' : 'normal')
-      .text(word.content + ' ');
+      .attr('class', 'text-inner-tspan')
+      .attr('font-weight', word.type === 'strong' ? 'bold' : 'normal');
+    const special = [
+      '<',
+      '>',
+      '&',
+      '"',
+      "'",
+      '.',
+      ',',
+      ':',
+      ';',
+      '!',
+      '?',
+      '(',
+      ')',
+      '[',
+      ']',
+      '{',
+      '}',
+    ];
+    if (index !== 0 && special.includes(word.content)) {
+      innerTspan.text(word.content);
+    } else {
+      innerTspan.text(' ' + word.content);
+    }
   });
 }
 
diff --git a/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js b/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js
index 005dd42f6..5e74a9956 100644
--- a/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js
+++ b/packages/mermaid/src/rendering-util/handle-markdown-text.spec.js
@@ -169,6 +169,37 @@ it('markdownToLines - Mixed formatting', () => {
   expect(output).toEqual(expectedOutput);
 });
 
+it('markdownToLines - Mixed formatting', () => {
+  const input = `The dog in **the** hog... a *very long text* about it
+Word!`;
+
+  const expectedOutput = [
+    [
+      { content: 'The', type: 'normal' },
+      { content: 'dog', type: 'normal' },
+      { content: 'in', type: 'normal' },
+      { content: 'the', type: 'strong' },
+      { content: 'hog', type: 'normal' },
+      { content: '.', type: 'normal' },
+      { content: '.', type: 'normal' },
+      { content: '.', type: 'normal' },
+      { content: 'a', type: 'normal' },
+      { content: 'very', type: 'em' },
+      { content: 'long', type: 'em' },
+      { content: 'text', type: 'em' },
+      { content: 'about', type: 'normal' },
+      { content: 'it', type: 'normal' },
+    ],
+    [
+      { content: 'Word', type: 'normal' },
+      { content: '!', type: 'normal' },
+    ],
+  ];
+
+  const output = markdownToLines(input);
+  expect(output).toEqual(expectedOutput);
+});
+
 test('markdownToHTML - Basic test', () => {
   const input = `This is regular text
 Here is a new line

From 89193d7360b68f4b431d0a9c04f1fc29ccaeb746 Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Wed, 29 Mar 2023 09:35:42 +0200
Subject: [PATCH 06/11] Simple markdown in lock file

---
 pnpm-lock.yaml | 41 ++++++-----------------------------------
 1 file changed, 6 insertions(+), 35 deletions(-)

diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 485f97720..792b7f890 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -121,6 +121,7 @@ importers:
   packages/mermaid:
     specifiers:
       '@braintree/sanitize-url': ^6.0.0
+      '@khanacademy/simple-markdown': ^0.8.6
       '@types/cytoscape': ^3.19.9
       '@types/d3': ^7.4.0
       '@types/dompurify': ^2.4.0
@@ -172,6 +173,7 @@ importers:
       web-worker: ^1.2.0
     dependencies:
       '@braintree/sanitize-url': 6.0.0
+      '@khanacademy/simple-markdown': 0.8.6_wcqkhtmu7mswc6yz4uyexck3ty
       cytoscape: 3.23.0
       cytoscape-cose-bilkent: 4.1.0_cytoscape@3.23.0
       cytoscape-fcose: 2.1.0_cytoscape@3.23.0
@@ -220,7 +222,7 @@ importers:
       typedoc-plugin-markdown: 3.13.6_typedoc@0.23.18
       typescript: 4.8.4
       unist-util-flatmap: 1.0.0
-      vitepress: 1.0.0-alpha.46_tbpndr44ulefs3hehwpi2mkf2y
+      vitepress: 1.0.0-alpha.46_hoyvfk3ab7nzsjkhptt6ai7rzq
       vitepress-plugin-search: 1.0.4-alpha.19_g67lr3vgasogkevpbew55lljzq
 
   packages/mermaid-example-diagram:
@@ -3415,7 +3417,7 @@ packages:
       '@vue/shared': 3.2.45
       estree-walker: 2.0.2
       magic-string: 0.25.9
-      postcss: 8.4.20
+      postcss: 8.4.21
       source-map: 0.6.1
     dev: true
 
@@ -4063,7 +4065,7 @@ packages:
   /axios/0.21.4_debug@4.3.2:
     resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
     dependencies:
-      follow-redirects: 1.15.2_debug@4.3.2
+      follow-redirects: 1.15.2_debug@4.3.4
     transitivePeerDependencies:
       - debug
     dev: true
@@ -6639,28 +6641,6 @@ packages:
     resolution: {integrity: sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==}
     dev: true
 
-  /follow-redirects/1.15.2:
-    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
-    engines: {node: '>=4.0'}
-    peerDependencies:
-      debug: '*'
-    peerDependenciesMeta:
-      debug:
-        optional: true
-    dev: true
-
-  /follow-redirects/1.15.2_debug@4.3.2:
-    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
-    engines: {node: '>=4.0'}
-    peerDependencies:
-      debug: '*'
-    peerDependenciesMeta:
-      debug:
-        optional: true
-    dependencies:
-      debug: 4.3.2
-    dev: true
-
   /follow-redirects/1.15.2_debug@4.3.4:
     resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
     engines: {node: '>=4.0'}
@@ -7210,7 +7190,7 @@ packages:
     engines: {node: '>=8.0.0'}
     dependencies:
       eventemitter3: 4.0.7
-      follow-redirects: 1.15.2
+      follow-redirects: 1.15.2_debug@4.3.4
       requires-port: 1.0.0
     transitivePeerDependencies:
       - debug
@@ -9775,15 +9755,6 @@ packages:
     resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
     dev: true
 
-  /postcss/8.4.20:
-    resolution: {integrity: sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==}
-    engines: {node: ^10 || ^12 || >=14}
-    dependencies:
-      nanoid: 3.3.4
-      picocolors: 1.0.0
-      source-map-js: 1.0.2
-    dev: true
-
   /postcss/8.4.21:
     resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
     engines: {node: ^10 || ^12 || >=14}

From 4caf7d7c7b25976fbea9126633b33a2223477370 Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Wed, 29 Mar 2023 16:01:08 +0200
Subject: [PATCH 07/11] Adding support for markdown string in flowchart-elk

---
 cypress/platform/knsv2.html                   | 67 +++++++++-------
 docs/config/setup/modules/defaultConfig.md    |  2 +-
 packages/mermaid/src/Diagram.ts               |  1 -
 packages/mermaid/src/config.type.ts           |  1 +
 packages/mermaid/src/dagre-wrapper/edges.js   |  6 +-
 packages/mermaid/src/dagre-wrapper/nodes.js   |  1 +
 .../mermaid/src/dagre-wrapper/shapes/util.js  |  4 +-
 packages/mermaid/src/defaultConfig.ts         | 12 +++
 .../flowchart/elk/flowRenderer-elk.js         | 79 ++++++++++---------
 .../src/diagrams/flowchart/elk/styles.ts      |  7 +-
 .../mermaid/src/diagrams/flowchart/flowDb.js  |  2 +
 .../src/diagrams/flowchart/parser/flow.jison  |  8 +-
 .../mermaid/src/rendering-util/createText.js  | 40 ++++++++--
 13 files changed, 147 insertions(+), 83 deletions(-)

diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html
index 69eab8b62..02a5ff5e6 100644
--- a/cypress/platform/knsv2.html
+++ b/cypress/platform/knsv2.html
@@ -57,51 +57,64 @@
     
   
   
-    
+    
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 %%
-graph BT
-a("`The **cat** in the hat} -- 1o --> b
-a -- 2o --> c
-a -- 3o --> d
-g --2i--> a
-d --1i--> a
-h --3i -->a
-b --> d(The dog in the hog)
-c --> d
-%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
-    
+graph LR + a{"`The **cat** in the hat`"} -- 1o --> b + a -- 2o --> c + a -- 3o --> d + g --2i--> a + d --1i--> a + h --3i -->a + b --> d(The dog in the hog) + c --> d +
+      %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 flowchart LR
-b("`The dog in **the** hog... a a a a *very long text* about it
+b("`The dog in **the** hog.(1)
+NL`") --"`1o **bold**`"--> c
+
+
+flowchart-elk LR
+b("`The dog in **the** hog.(1)
+NL`") --"`1o **bold**`"--> c
+
+
+flowchart-elk LR
+b("`The dog in **the** hog.(1).. a a a a *very long text* about it
 Word!
 
-Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `")
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `") --> c
 
       %%{init: {"flowchart": {"htmlLabels": true}} }%%
-flowchart LR
-b("`The dog in **the** hog... a a a a *very long text* about it
+flowchart-elk LR
+b("`The dog in **the** hog(2)... a a a a *very long text* about it
 Word!
 Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `")
 
       %%{init: {"flowchart": {"htmlLabels": false}} }%%
-flowchart LR
+flowchart-elk LR
 b("The dog in the hog... a very
long text about it
Word!")
       %%{init: {"flowchart": {"htmlLabels": true}} }%%
-flowchart LR
+flowchart-elk LR
 b("The dog in the hog... a very
long text about it
Word!")
-flowchart LR
+flowchart-elk LR
 subgraph "One"
   a("`The **cat**
-  in the hat`") -- 1o --> b{{"`The **dog** in the hog`"}}
+  in the hat`") -- "1o" --> b{{"`The **dog** in the hog`"}}
 end
 subgraph "`**Two**`"
   c("`The **cat**
@@ -129,7 +142,7 @@ mindmap
       id2["`The dog in **the** hog... a *very long text* about it
 Word!`"]
     
-
+    
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 flowchart TB
   %% I could not figure out how to use double quotes in labels in Mermaid
@@ -185,7 +198,7 @@ flowchart TB
 

-
+    
 flowchart TB
   %% I could not figure out how to use double quotes in labels in Mermaid
   subgraph ibm[IBM Espresso CPU]
@@ -241,7 +254,7 @@ flowchart TB
     >
     
  -
+    
       flowchart LR
   B1 --be be--x B2
   B1 --bo bo--o B3
@@ -274,7 +287,7 @@ flowchart TB
   B6 --> B5
   
-
+    
 sequenceDiagram
     Customer->>+Stripe: Makes a payment request
     Stripe->>+Bank: Forwards the payment request to the bank
@@ -287,7 +300,7 @@ sequenceDiagram
     Customer->>+Merchant: Receives goods or services
         
-
+    
 mindmap
   root((mindmap))
     Origins
@@ -307,7 +320,7 @@ mindmap
       Mermaid
     

-
+    
   example-diagram
     
@@ -332,8 +345,8 @@ mindmap flowchart: { // defaultRenderer: 'elk', useMaxWidth: false, - // htmlLabels: false, htmlLabels: false, + // htmlLabels: true, }, htmlLabels: false, gantt: { diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index 3b2e33842..6493a93db 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -14,7 +14,7 @@ #### Defined in -[defaultConfig.ts:2093](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2093) +[defaultConfig.ts:2105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2105) --- diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts index 3943a84c2..8e924f2fa 100644 --- a/packages/mermaid/src/Diagram.ts +++ b/packages/mermaid/src/Diagram.ts @@ -44,7 +44,6 @@ export class Diagram { // calls diagram.db.clear(), which would reset anything set by // extractFrontMatter(). this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db)); - // this.parser.parse = (text: string) => { // console.log('parse called'); // try { diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 157304149..c3ab7671e 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -385,6 +385,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig { curve?: string; padding?: number; defaultRenderer?: string; + wrappingWidth?: number; } export interface FontConfig { diff --git a/packages/mermaid/src/dagre-wrapper/edges.js b/packages/mermaid/src/dagre-wrapper/edges.js index 4f83e938a..d4b7d37b8 100644 --- a/packages/mermaid/src/dagre-wrapper/edges.js +++ b/packages/mermaid/src/dagre-wrapper/edges.js @@ -19,7 +19,11 @@ export const insertEdgeLabel = (elem, edge) => { // Create the actual text element const labelElement = edge.labelType === 'markdown' - ? createText(elem, edge.label, { style: edge.labelStyle, useHtmlLabels }) + ? createText(elem, edge.label, { + style: edge.labelStyle, + useHtmlLabels, + addSvgBackground: true, + }) : createLabel(edge.label, edge.labelStyle); log.info('abc82', edge, edge.labelType); diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js index 3e71f500a..dd0225770 100644 --- a/packages/mermaid/src/dagre-wrapper/nodes.js +++ b/packages/mermaid/src/dagre-wrapper/nodes.js @@ -997,6 +997,7 @@ export const insertNode = (elem, node, dir) => { el.attr('class', 'node default ' + node.class); } + /* MC: 7e790808-9c49-4f74-93de-15c22872377f */ nodeElems[node.id] = newEl; if (node.haveCallback) { diff --git a/packages/mermaid/src/dagre-wrapper/shapes/util.js b/packages/mermaid/src/dagre-wrapper/shapes/util.js index d362d08f6..b5de2bd6b 100644 --- a/packages/mermaid/src/dagre-wrapper/shapes/util.js +++ b/packages/mermaid/src/dagre-wrapper/shapes/util.js @@ -34,7 +34,7 @@ export const labelHelper = (parent, node, _classes, isNode) => { // text = textNode; text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), { useHtmlLabels: getConfig().flowchart.htmlLabels, - width: node.width || 200, + width: node.width || getConfig().flowchart.wrappingWidth, classes: 'markdown-node-label', }); } else { @@ -67,7 +67,7 @@ export const labelHelper = (parent, node, _classes, isNode) => { } else { label.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')'); } - + label.insert('rect', ':first-child'); return { shapeSvg, bbox, halfPadding, label }; }; diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 666efc364..4d8ebf4e8 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -258,6 +258,18 @@ const config: Partial = { * Default value: 'dagre-wrapper' */ defaultRenderer: 'dagre-wrapper', + /** + * | Parameter | Description | Type | Required | Values | + * | --------------- | ----------- | ------- | -------- | ----------------------- | + * | wrappingWidth | See notes | number | 4 | width of nodes where text is wrapped | + * + * **Notes:** + * + * When using markdown strings the text ius wrapped automatically, this + * value sets the max width of a text before it continues on a new line. + * Default value: 'dagre-wrapper' + */ + wrappingWidth: 200, }, /** The object containing configurations specific for sequence diagrams */ diff --git a/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js b/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js index 426d22dbb..1b1dd3c50 100644 --- a/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js +++ b/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js @@ -3,6 +3,7 @@ import { insertNode } from '../../../dagre-wrapper/nodes.js'; import insertMarkers from '../../../dagre-wrapper/markers.js'; import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js'; import { findCommonAncestor } from './render-utils'; +import { labelHelper } from '../../../dagre-wrapper/shapes/util'; import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js'; import { getConfig } from '../../../config'; import { log } from '../../../logger'; @@ -52,7 +53,7 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook if (vertex.classes.length > 0) { classStr = vertex.classes.join(' '); } - + classStr = classStr + ' flowchart-label'; const styles = getStylesFromArray(vertex.styles); // Use vertex id as text in the box if no text is provided by the graph definition @@ -61,40 +62,6 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook // We create a SVG label, either by delegating to addHtmlLabel or manually let vertexNode; const labelData = { width: 0, height: 0 }; - if (evaluate(getConfig().flowchart.htmlLabels)) { - // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? - const node = { - label: vertexText.replace( - /fa[blrs]?:fa-[\w-]+/g, - (s) => `` - ), - }; - vertexNode = addHtmlLabel(svg, node).node(); - const bbox = vertexNode.getBBox(); - labelData.width = bbox.width; - labelData.height = bbox.height; - labelData.labelNode = vertexNode; - vertexNode.parentNode.removeChild(vertexNode); - } else { - const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text'); - svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:')); - - const rows = vertexText.split(common.lineBreakRegex); - - for (const row of rows) { - const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan'); - tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); - tspan.setAttribute('dy', '1em'); - tspan.setAttribute('x', '1'); - tspan.textContent = row; - svgLabel.appendChild(tspan); - } - vertexNode = svgLabel; - const bbox = vertexNode.getBBox(); - labelData.width = bbox.width; - labelData.height = bbox.height; - labelData.labelNode = vertexNode; - } const ports = [ { @@ -186,11 +153,13 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook default: _shape = 'rect'; } + // Add the node const node = { labelStyle: styles.labelStyle, shape: _shape, labelText: vertexText, + labelType: vertex.labelType, rx: radious, ry: radious, class: classStr, @@ -209,10 +178,33 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook }; let boundingBox; let nodeEl; + + // Add the element to the DOM if (node.type !== 'group') { nodeEl = insertNode(nodes, node, vertex.dir); boundingBox = nodeEl.node().getBBox(); + } else { + const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text'); + // svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:')); + // const rows = vertexText.split(common.lineBreakRegex); + // for (const row of rows) { + // const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan'); + // tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); + // tspan.setAttribute('dy', '1em'); + // tspan.setAttribute('x', '1'); + // tspan.textContent = row; + // svgLabel.appendChild(tspan); + // } + // vertexNode = svgLabel; + // const bbox = vertexNode.getBBox(); + const { shapeSvg, bbox } = labelHelper(nodes, node, undefined, true); + labelData.width = bbox.width; + labelData.wrappingWidth = getConfig().flowchart.wrappingWidth; + labelData.height = bbox.height; + labelData.labelNode = shapeSvg.node(); + node.labelData = labelData; } + // const { shapeSvg, bbox } = labelHelper(svg, node, undefined, true); const data = { id: vertex.id, @@ -520,7 +512,7 @@ export const addEdges = function (edges, diagObj, graph, svg) { edgeData.labelpos = 'c'; } - edgeData.labelType = 'text'; + edgeData.labelType = edge.labelType; edgeData.label = edge.text.replace(common.lineBreakRegex, '\n'); if (edge.style === undefined) { @@ -845,9 +837,17 @@ export const draw = async function (text, id, _version, diagObj) { log.info('Subgraphs - ', subGraphs); for (let i = subGraphs.length - 1; i >= 0; i--) { subG = subGraphs[i]; - diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir); + diagObj.db.addVertex( + subG.id, + { text: subG.title, type: subG.labelType }, + 'group', + undefined, + subG.classes, + subG.dir + ); } + // debugger; // Add an element in the svg to be used to hold the subgraphs container // elements const subGraphsEl = svg.insert('g').attr('class', 'subgraphs'); @@ -860,7 +860,7 @@ export const draw = async function (text, id, _version, diagObj) { // in order to get the size of the node. You can't get the size of a node // that is not in the dom so we need to add it to the dom, get the size // we will position the nodes when we get the layout from elkjs - graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph); + graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph, svg); // Time for the edges, we start with adding an element in the node to hold the edges const edgesEl = svg.insert('g').attr('class', 'edges edgePath'); @@ -887,6 +887,8 @@ export const draw = async function (text, id, _version, diagObj) { }, width: node.labelData.width, height: node.labelData.height, + // width: 100, + // height: 100, }, ]; delete node.x; @@ -895,6 +897,7 @@ export const draw = async function (text, id, _version, diagObj) { delete node.height; } }); + insertChildren(graph.children, parentLookupDb); log.info('after layout', JSON.stringify(graph, null, 2)); const g = await elk.layout(graph); diff --git a/packages/mermaid/src/diagrams/flowchart/elk/styles.ts b/packages/mermaid/src/diagrams/flowchart/elk/styles.ts index e8e7065a0..60659df45 100644 --- a/packages/mermaid/src/diagrams/flowchart/elk/styles.ts +++ b/packages/mermaid/src/diagrams/flowchart/elk/styles.ts @@ -81,7 +81,7 @@ const getStyles = (options: FlowChartStyleOptions) => .edgeLabel { background-color: ${options.edgeLabelBackground}; rect { - opacity: 0.5; + opacity: 0.85; background-color: ${options.edgeLabelBackground}; fill: ${options.edgeLabelBackground}; } @@ -132,6 +132,11 @@ const getStyles = (options: FlowChartStyleOptions) => // fill:#ccc; // // stroke:black; // } + + .flowchart-label text { + text-anchor: middle; + } + ${genSections(options)} `; diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index 26bc75c14..85dc5337e 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -143,6 +143,7 @@ export const addSingleLink = function (_start, _end, type) { // log.info('Got edge...', start, end); const edge = { start: start, end: end, type: undefined, text: '', labelType: 'text' }; + log.info('abc78 Got edge...', edge); const linkTextObj = type.text; if (linkTextObj !== undefined) { @@ -163,6 +164,7 @@ export const addSingleLink = function (_start, _end, type) { edges.push(edge); }; export const addLink = function (_start, _end, type) { + log.info('addLink (abc78)', _start, _end, type); let i, j; for (i = 0; i < _start.length; i++) { for (j = 0; j < _end.length; j++) { diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index d21104d49..19ab80b05 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -438,13 +438,13 @@ arrowText: ; text: textToken - {$$={text:$1, type: 'text'};} + { $$={text:$1, type: 'text'};} | text textToken - {$$={text:$1.text+''+$2, type: 'text'};} + { $$={text:$1.text+''+$2, type: $1.type};} | STR - {$$={text: $1, type: 'text'};} + { $$={text: $1, type: 'text'};} | MD_STR - {$$={text: $1, type: 'markdown'};} + { $$={text: $1, type: 'markdown'};} ; diff --git a/packages/mermaid/src/rendering-util/createText.js b/packages/mermaid/src/rendering-util/createText.js index 0fc7dda54..1097cd0df 100644 --- a/packages/mermaid/src/rendering-util/createText.js +++ b/packages/mermaid/src/rendering-util/createText.js @@ -83,11 +83,13 @@ function createTspan(textElement, lineIndex, lineHeight) { * @param {number} width - The maximum allowed width of the text. * @param {object} g - The parent group element to append the formatted text. * @param {Array} structuredText - The structured text data to format. + * @param addBackground */ -function createFormattedText(width, g, structuredText) { +function createFormattedText(width, g, structuredText, addBackground = false) { const lineHeight = 1.1; - - const textElement = g.append('text').attr('y', '-10.1'); + const labelGroup = g.append('g'); + let bkg = labelGroup.insert('rect').attr('class', 'background'); + const textElement = labelGroup.append('text').attr('y', '-10.1'); // .attr('dominant-baseline', 'middle') // .attr('text-anchor', 'middle'); // .attr('text-anchor', 'middle'); @@ -118,8 +120,20 @@ function createFormattedText(width, g, structuredText) { } } }); - return textElement.node(); - // return g.node(); + if (addBackground) { + const bbox = textElement.node().getBBox(); + const padding = 2; + bkg + .attr('x', -padding) + .attr('y', -padding) + .attr('width', bbox.width + 2 * padding) + .attr('height', bbox.height + 2 * padding); + // .style('fill', 'red'); + + return labelGroup.node(); + } else { + return textElement.node(); + } } /** @@ -183,9 +197,17 @@ function updateTextContentAndStyles(tspan, wrappedLine) { export const createText = ( el, text = '', - { style = '', isTitle = false, classes = '', useHtmlLabels = true, isNode = true, width } = {} + { + style = '', + isTitle = false, + classes = '', + useHtmlLabels = true, + isNode = true, + width, + addSvgBackground = false, + } = {} ) => { - log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode); + log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground); if (useHtmlLabels) { // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? // text = text.replace(/\\n|\n/g, '
'); @@ -203,6 +225,8 @@ export const createText = ( return vertexNode; } else { const structuredText = markdownToLines(text); - return createFormattedText(width, el, structuredText); + + const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground); + return svgLabel; } }; From 507a518a91dad6dee6da90952cb4ca19bd5286e6 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Wed, 29 Mar 2023 16:17:30 +0200 Subject: [PATCH 08/11] Bugfix for port selection when using diamonds in elk layout of flowcharts --- .../mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js b/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js index 1b1dd3c50..5849177b9 100644 --- a/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js +++ b/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js @@ -13,7 +13,7 @@ import { interpolateToCurve, getStylesFromArray } from '../../../utils'; import ELK from 'elkjs/lib/elk.bundled.js'; const elk = new ELK(); -const portPos = {}; +let portPos = {}; const conf = {}; export const setConf = function (cnf) { @@ -767,6 +767,7 @@ export const draw = async function (text, id, _version, diagObj) { // Add temporary render element diagObj.db.clear(); nodeDb = {}; + portPos = {}; diagObj.db.setGen('gen-2'); // Parse the graph definition diagObj.parser.parse(text); From 99f65813a1bcf7c36d5107c9d1b7e05d38096b98 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Mon, 3 Apr 2023 10:43:15 +0200 Subject: [PATCH 09/11] Syntax for markdown strings is a single backtick. --- cypress/platform/knsv2.html | 70 ++++++++++++------- .../interfaces/mermaidAPI.ParseOptions.md | 2 +- .../interfaces/mermaidAPI.RenderResult.md | 4 +- docs/config/setup/modules/mermaidAPI.md | 22 +++--- packages/mermaid/src/dagre-wrapper/nodes.js | 1 - .../src/diagrams/flowchart/parser/flow.jison | 6 +- .../src/diagrams/mindmap/parser/mindmap.jison | 6 +- packages/mermaid/src/mermaidAPI.ts | 9 ++- 8 files changed, 73 insertions(+), 47 deletions(-) diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 02a5ff5e6..4c160ca1b 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -58,10 +58,30 @@
+flowchart LR
+    A:::someclass --> B[`The **cat** in the hat`]:::someclass
+    id1(Start)-->id2(Stop)
+    style id1 fill:#f9f,stroke:#333,stroke-width:4px
+    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+    classDef someclass fill:#f96
+
+
+%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
+%%
+flowchart LR
+    A:::someclass --> B[`The **cat** in the hat`]:::someclass
+    id1(Start)-->id2(Stop)
+    style id1 fill:#f9f,stroke:#333,stroke-width:4px
+    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+    classDef someclass fill:#f96
+
+
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 %%
 graph LR
-  a{"`The **cat** in the hat`"} -- 1o --> b
+  a{`The **cat** in the hat`} -- 1o --> b
   a -- 2o --> c
   a -- 3o --> d
   g --2i--> a
@@ -74,30 +94,30 @@ graph LR
     
       %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 flowchart LR
-b("`The dog in **the** hog.(1)
-NL`") --"`1o **bold**`"--> c
+b(`The dog in **the** hog.(1)
+NL`) --`1o **bold**`--> c
 
 flowchart-elk LR
-b("`The dog in **the** hog.(1)
-NL`") --"`1o **bold**`"--> c
+b(`The dog in **the** hog.(1)
+NL`) --`1o **bold**`--> c
 
 flowchart-elk LR
-b("`The dog in **the** hog.(1).. a a a a *very long text* about it
+b(`The dog in **the** hog.(1).. a a a a *very long text* about it
 Word!
 
-Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `") --> c
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `) --> c
 
       %%{init: {"flowchart": {"htmlLabels": true}} }%%
 flowchart-elk LR
-b("`The dog in **the** hog(2)... a a a a *very long text* about it
+b(`The dog in **the** hog(2)... a a a a *very long text* about it
 Word!
-Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `")
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `)
 
@@ -113,34 +133,34 @@ b("The dog in the hog... a very
long text about it
Word!")
 flowchart-elk LR
 subgraph "One"
-  a("`The **cat**
-  in the hat`") -- "1o" --> b{{"`The **dog** in the hog`"}}
+  a(`The **cat**
+  in the hat`) -- "1o" --> b{{`The **dog** in the hog`}}
 end
-subgraph "`**Two**`"
-  c("`The **cat**
-  in the hat`") -- "`1o **ipa**`" --> d("The dog in the hog")
+subgraph `**Two**`
+  c(`The **cat**
+  in the hat`) -- `1o **ipa**` --> d("The dog in the hog")
 end
 
 mindmap
-    id1["`**Start2**
-    second line 😎 with long text that is wrapping to the next line`"]
-      id2["`Child **with bold** text`"]
-      id3["`Children of which some
-      is using *italic type of* text`"]
+    id1[`**Start2**
+    second line 😎 with long text that is wrapping to the next line`]
+      id2[`Child **with bold** text`]
+      id3[`Children of which some
+      is using *italic type of* text`]
       id4[Child]
-      id5["`Child
+      id5[`Child
       Row
       and another
-      `"]
+      `]
     
 mindmap
-    id1["`**Start** with
-    a second line 😎`"]
-      id2["`The dog in **the** hog... a *very long text* about it
-Word!`"]
+    id1[`**Start** with
+    a second line 😎`]
+      id2[`The dog in **the** hog... a *very long text* about it
+Word!`]
     
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
diff --git a/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md b/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md
index 93708863c..8ab259885 100644
--- a/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md
+++ b/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md
@@ -16,4 +16,4 @@
 
 #### Defined in
 
-[mermaidAPI.ts:70](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L70)
+[mermaidAPI.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L77)
diff --git a/docs/config/setup/interfaces/mermaidAPI.RenderResult.md b/docs/config/setup/interfaces/mermaidAPI.RenderResult.md
index ec59f8796..f84a51b87 100644
--- a/docs/config/setup/interfaces/mermaidAPI.RenderResult.md
+++ b/docs/config/setup/interfaces/mermaidAPI.RenderResult.md
@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
 
 #### Defined in
 
-[mermaidAPI.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L91)
+[mermaidAPI.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L98)
 
 ---
 
@@ -51,4 +51,4 @@ The svg code for the rendered graph.
 
 #### Defined in
 
-[mermaidAPI.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L81)
+[mermaidAPI.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L88)
diff --git a/docs/config/setup/modules/mermaidAPI.md b/docs/config/setup/modules/mermaidAPI.md
index b94fc8b94..e2f036785 100644
--- a/docs/config/setup/modules/mermaidAPI.md
+++ b/docs/config/setup/modules/mermaidAPI.md
@@ -25,7 +25,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
 
 #### Defined in
 
-[mermaidAPI.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L75)
+[mermaidAPI.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L82)
 
 ## Variables
 
@@ -95,7 +95,7 @@ mermaid.initialize(config);
 
 #### Defined in
 
-[mermaidAPI.ts:662](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L662)
+[mermaidAPI.ts:669](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L669)
 
 ## Functions
 
@@ -126,7 +126,7 @@ Return the last node appended
 
 #### Defined in
 
-[mermaidAPI.ts:305](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L305)
+[mermaidAPI.ts:312](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L312)
 
 ---
 
@@ -152,7 +152,7 @@ the cleaned up svgCode
 
 #### Defined in
 
-[mermaidAPI.ts:256](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L256)
+[mermaidAPI.ts:263](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L263)
 
 ---
 
@@ -178,7 +178,7 @@ the string with all the user styles
 
 #### Defined in
 
-[mermaidAPI.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L185)
+[mermaidAPI.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L192)
 
 ---
 
@@ -201,7 +201,7 @@ the string with all the user styles
 
 #### Defined in
 
-[mermaidAPI.ts:233](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L233)
+[mermaidAPI.ts:240](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L240)
 
 ---
 
@@ -228,7 +228,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
 
 #### Defined in
 
-[mermaidAPI.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L169)
+[mermaidAPI.ts:176](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L176)
 
 ---
 
@@ -248,7 +248,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
 
 #### Defined in
 
-[mermaidAPI.ts:149](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L149)
+[mermaidAPI.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L156)
 
 ---
 
@@ -268,7 +268,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
 
 #### Defined in
 
-[mermaidAPI.ts:120](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L120)
+[mermaidAPI.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L127)
 
 ---
 
@@ -294,7 +294,7 @@ Put the svgCode into an iFrame. Return the iFrame code
 
 #### Defined in
 
-[mermaidAPI.ts:284](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L284)
+[mermaidAPI.ts:291](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L291)
 
 ---
 
@@ -319,4 +319,4 @@ Remove any existing elements from the given document
 
 #### Defined in
 
-[mermaidAPI.ts:355](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L355)
+[mermaidAPI.ts:362](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L362)
diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js
index dd0225770..3e71f500a 100644
--- a/packages/mermaid/src/dagre-wrapper/nodes.js
+++ b/packages/mermaid/src/dagre-wrapper/nodes.js
@@ -997,7 +997,6 @@ export const insertNode = (elem, node, dir) => {
     el.attr('class', 'node default ' + node.class);
   }
 
-  /* MC: 7e790808-9c49-4f74-93de-15c22872377f */
   nodeElems[node.id] = newEl;
 
   if (node.haveCallback) {
diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
index 19ab80b05..dd82cff4d 100644
--- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
+++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
@@ -38,9 +38,9 @@ accDescr\s*"{"\s*                                { this.begin("acc_descr_multili
 [\}]                       { this.popState(); }
 [^\}]*                     return "acc_descr_multiline_value";
 // .*[^\n]*                    {  return "acc_descr_line"}
-["][`]          { this.begin("md_string");}
-[^`"]+        { return "MD_STR";}
-[`]["]          { this.popState();}
+[`]          { this.begin("md_string");}
+[^`]+        { return "MD_STR";}
+[`]          { this.popState();}
 ["]                     this.begin("string");
 ["]             this.popState();
 [^"]*           return "STR";
diff --git a/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison b/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison
index 84a6191cf..41ee33c91 100644
--- a/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison
+++ b/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison
@@ -42,9 +42,9 @@
 // !(-\()            return 'NODE_ID';
 [^\(\[\n\-\)\{\}]+         return 'NODE_ID';
 <>            return 'EOF';
-["][`]          { this.begin("NSTR2");}
-[^`"]+        { return "NODE_DESCR";}
-[`]["]          { this.popState();}
+[`]          { this.begin("NSTR2");}
+[^`]+        { return "NODE_DESCR";}
+[`]          { this.popState();}
 ["]          { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");}
 [^"]+        { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";}
 ["]          {this.popState();}
diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts
index dba629477..039e8c596 100644
--- a/packages/mermaid/src/mermaidAPI.ts
+++ b/packages/mermaid/src/mermaidAPI.ts
@@ -32,7 +32,14 @@ import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility'
 import { parseDirective } from './directiveUtils';
 
 // diagram names that support classDef statements
-const CLASSDEF_DIAGRAMS = ['graph', 'flowchart', 'flowchart-v2', 'stateDiagram', 'stateDiagram-v2'];
+const CLASSDEF_DIAGRAMS = [
+  'graph',
+  'flowchart',
+  'flowchart-v2',
+  'flowchart-elk',
+  'stateDiagram',
+  'stateDiagram-v2',
+];
 const MAX_TEXTLENGTH = 50_000;
 const MAX_TEXTLENGTH_EXCEEDED_MSG =
   'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';

From 471c842a58204d19f1e71492db09474b6025ec2a Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Mon, 3 Apr 2023 12:12:51 +0200
Subject: [PATCH 10/11] Adding rendering tests and unit tests

---
 .../rendering/flowchart-elk.spec.js           | 145 ++++++++++++++++++
 .../rendering/flowchart-v2.spec.js            | 145 ++++++++++++++++++
 cypress/integration/rendering/mindmap.spec.ts |  13 ++
 cypress/platform/knsv2.html                   |  98 ++----------
 .../flowchart/parser/flow-md-string.spec.js   |  64 ++++++++
 .../src/diagrams/flowchart/parser/flow.jison  |   6 +-
 .../src/diagrams/mindmap/mindmapRenderer.js   |   5 +-
 .../src/diagrams/mindmap/parser/mindmap.jison |   6 +-
 8 files changed, 392 insertions(+), 90 deletions(-)
 create mode 100644 packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js

diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js
index 414037651..68d8b3ce5 100644
--- a/cypress/integration/rendering/flowchart-elk.spec.js
+++ b/cypress/integration/rendering/flowchart-elk.spec.js
@@ -684,4 +684,149 @@ A --> B
       { titleTopMargin: 0 }
     );
   });
+  describe('Markdown strings flowchart-elk (#4220)', () => {
+    describe('html labels', () => {
+      it('With styling and classes', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart-elk LR
+    A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
+    id1(Start)-->id2(Stop)
+    style id1 fill:#f9f,stroke:#333,stroke-width:4px
+    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+    classDef someclass fill:#f96
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('With formatting in a node', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart-elk LR
+  a{"\`The **cat** in the hat\`"} -- 1o --> b
+  a -- 2o --> c
+  a -- 3o --> d
+  g --2i--> a
+  d --1i--> a
+  h --3i -->a
+  b --> d(The dog in the hog)
+  c --> d
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('New line in node and formatted edge label', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart-elk LR
+b("\`The dog in **the** hog.(1)
+NL\`") --"\`1o **bold**\`"--> c
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Wrapping long text with a new line', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart-elk LR
+b(\`The dog in **the** hog.(1).. a a a a *very long text* about it
+Word!
+
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`) --> c
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Sub graphs and markdown strings', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart-elk LR
+subgraph "One"
+  a("\`The **cat**
+  in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
+end
+subgraph "\`**Two**\`"
+  c("\`The **cat**
+  in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
+end
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+    });
+
+    describe('svg text labels', () => {
+      it('With styling and classes', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart-elk LR
+    A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
+    id1(Start)-->id2(Stop)
+    style id1 fill:#f9f,stroke:#333,stroke-width:4px
+    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+    classDef someclass fill:#f96
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('With formatting in a node', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart-elk LR
+  a{"\`The **cat** in the hat\`"} -- 1o --> b
+  a -- 2o --> c
+  a -- 3o --> d
+  g --2i--> a
+  d --1i--> a
+  h --3i -->a
+  b --> d(The dog in the hog)
+  c --> d
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('New line in node and formatted edge label', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart-elk LR
+b("\`The dog in **the** hog.(1)
+NL\`") --"\`1o **bold**\`"--> c
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Wrapping long text with a new line', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart-elk LR
+b("\`The dog in **the** hog.(1).. a a a a *very long text* about it
+Word!
+
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Sub graphs and markdown strings', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart-elk LR
+subgraph "One"
+  a("\`The **cat**
+  in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
+end
+subgraph "\`**Two**\`"
+  c("\`The **cat**
+  in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
+end
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+    });
+  });
 });
diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js
index abdb22265..4513cc87d 100644
--- a/cypress/integration/rendering/flowchart-v2.spec.js
+++ b/cypress/integration/rendering/flowchart-v2.spec.js
@@ -685,4 +685,149 @@ A ~~~ B
       { titleTopMargin: 0 }
     );
   });
+  describe('Markdown strings flowchart (#4220)', () => {
+    describe('html labels', () => {
+      it('With styling and classes', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart LR
+    A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
+    id1(Start)-->id2(Stop)
+    style id1 fill:#f9f,stroke:#333,stroke-width:4px
+    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+    classDef someclass fill:#f96
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('With formatting in a node', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart LR
+  a{"\`The **cat** in the hat\`"} -- 1o --> b
+  a -- 2o --> c
+  a -- 3o --> d
+  g --2i--> a
+  d --1i--> a
+  h --3i -->a
+  b --> d(The dog in the hog)
+  c --> d
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('New line in node and formatted edge label', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart LR
+b("\`The dog in **the** hog.(1)
+NL\`") --"\`1o **bold**\`"--> c
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Wrapping long text with a new line', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart LR
+b("\`The dog in **the** hog.(1).. a a a a *very long text* about it
+Word!
+
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Sub graphs and markdown strings', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": true}} }%%
+flowchart LR
+subgraph "One"
+  a("\`The **cat**
+  in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
+end
+subgraph "\`**Two**\`"
+  c("\`The **cat**
+  in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
+end
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+    });
+
+    describe('svg text labels', () => {
+      it('With styling and classes', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart LR
+    A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
+    id1(Start)-->id2(Stop)
+    style id1 fill:#f9f,stroke:#333,stroke-width:4px
+    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+    classDef someclass fill:#f96
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('With formatting in a node', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart LR
+  a{"\`The **cat** in the hat\`"} -- 1o --> b
+  a -- 2o --> c
+  a -- 3o --> d
+  g --2i--> a
+  d --1i--> a
+  h --3i -->a
+  b --> d(The dog in the hog)
+  c --> d
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('New line in node and formatted edge label', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart LR
+b("\`The dog in **the** hog.(1)
+NL\`") --"\`1o **bold**\`"--> c
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Wrapping long text with a new line', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart LR
+b("\`The dog in **the** hog.(1).. a a a a *very long text* about it
+Word!
+
+Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+      it('Sub graphs and markdown strings', () => {
+        imgSnapshotTest(
+          `%%{init: {"flowchart": {"htmlLabels": false}} }%%
+flowchart LR
+subgraph "One"
+  a("\`The **cat**
+  in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
+end
+subgraph "\`**Two**\`"
+  c("\`The **cat**
+  in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
+end
+
+`,
+          { titleTopMargin: 0 }
+        );
+      });
+    });
+  });
 });
diff --git a/cypress/integration/rendering/mindmap.spec.ts b/cypress/integration/rendering/mindmap.spec.ts
index 4663f6225..94b3f9ca0 100644
--- a/cypress/integration/rendering/mindmap.spec.ts
+++ b/cypress/integration/rendering/mindmap.spec.ts
@@ -223,5 +223,18 @@ mindmap
       shouldHaveRoot
     );
   });
+  describe('Markdown strings mindmaps (#4220)', () => {
+    it('Formatted label with linebreak and a wrapping label and emojis', () => {
+      imgSnapshotTest(
+        `mindmap
+    id1[\`**Start** with
+    a second line 😎\`]
+      id2[\`The dog in **the** hog... a *very long text* about it
+Word!\`]
+`,
+        { titleTopMargin: 0 }
+      );
+    });
+  });
   /* The end */
 });
diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html
index 4c160ca1b..08a4c0e68 100644
--- a/cypress/platform/knsv2.html
+++ b/cypress/platform/knsv2.html
@@ -58,91 +58,23 @@
   
   
     
-flowchart LR
-    A:::someclass --> B[`The **cat** in the hat`]:::someclass
-    id1(Start)-->id2(Stop)
-    style id1 fill:#f9f,stroke:#333,stroke-width:4px
-    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
-    classDef someclass fill:#f96
-
-
-%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
-%%
-flowchart LR
-    A:::someclass --> B[`The **cat** in the hat`]:::someclass
-    id1(Start)-->id2(Stop)
-    style id1 fill:#f9f,stroke:#333,stroke-width:4px
-    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
-    classDef someclass fill:#f96
-
-
-%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
-%%
-graph LR
-  a{`The **cat** in the hat`} -- 1o --> b
-  a -- 2o --> c
-  a -- 3o --> d
-  g --2i--> a
-  d --1i--> a
-  h --3i -->a
-  b --> d(The dog in the hog)
-  c --> d
-
-
-      %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
-flowchart LR
-b(`The dog in **the** hog.(1)
-NL`) --`1o **bold**`--> c
-
-
-flowchart-elk LR
-b(`The dog in **the** hog.(1)
-NL`) --`1o **bold**`--> c
-
-
-flowchart-elk LR
-b(`The dog in **the** hog.(1).. a a a a *very long text* about it
-Word!
-
-Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `) --> c
-
-
-      %%{init: {"flowchart": {"htmlLabels": true}} }%%
+      %%{init: {"flowchart": {"htmlLabels": false}} }%%
 flowchart-elk LR
 b(`The dog in **the** hog(2)... a a a a *very long text* about it
 Word!
 Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `)
 
-
-      %%{init: {"flowchart": {"htmlLabels": false}} }%%
+    
 flowchart-elk LR
 b("The dog in the hog... a very
long text about it
Word!")
-
-      %%{init: {"flowchart": {"htmlLabels": true}} }%%
+    
 flowchart-elk LR
 b("The dog in the hog... a very
long text about it
Word!")
-
-flowchart-elk LR
-subgraph "One"
-  a(`The **cat**
-  in the hat`) -- "1o" --> b{{`The **dog** in the hog`}}
-end
-subgraph `**Two**`
-  c(`The **cat**
-  in the hat`) -- `1o **ipa**` --> d("The dog in the hog")
-end
-
-
+    
 mindmap
     id1[`**Start2**
     second line 😎 with long text that is wrapping to the next line`]
@@ -157,12 +89,12 @@ mindmap
     
 mindmap
-    id1[`**Start** with
-    a second line 😎`]
-      id2[`The dog in **the** hog... a *very long text* about it
-Word!`]
+    id1["`**Start** with
+    a second line 😎`"]
+      id2["`The dog in **the** hog... a *very long text* about it
+Word!`"]
     
-
+    
 %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
 flowchart TB
   %% I could not figure out how to use double quotes in labels in Mermaid
@@ -218,7 +150,7 @@ flowchart TB
 

-
+    
 flowchart TB
   %% I could not figure out how to use double quotes in labels in Mermaid
   subgraph ibm[IBM Espresso CPU]
@@ -274,7 +206,7 @@ flowchart TB
     >
     
  -
+    
       flowchart LR
   B1 --be be--x B2
   B1 --bo bo--o B3
@@ -307,7 +239,7 @@ flowchart TB
   B6 --> B5
   
-
+    
 sequenceDiagram
     Customer->>+Stripe: Makes a payment request
     Stripe->>+Bank: Forwards the payment request to the bank
@@ -320,7 +252,7 @@ sequenceDiagram
     Customer->>+Merchant: Receives goods or services
         
-
+    
 mindmap
   root((mindmap))
     Origins
@@ -340,7 +272,7 @@ mindmap
       Mermaid
     

-
+    
   example-diagram
     
@@ -368,7 +300,7 @@ mindmap htmlLabels: false, // htmlLabels: true, }, - htmlLabels: false, + // htmlLabels: true, gantt: { useMaxWidth: false, }, diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js new file mode 100644 index 000000000..005d257e0 --- /dev/null +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js @@ -0,0 +1,64 @@ +import flowDb from '../flowDb'; +import flow from './flow'; +import { setConfig } from '../../../config'; + +setConfig({ + securityLevel: 'strict', +}); + +describe('parsing a flow chart with markdown strings', function () { + beforeEach(function () { + flow.parser.yy = flowDb; + flow.parser.yy.clear(); + }); + + it('mardown formatting in nodes and labels', function () { + const res = flow.parser.parse(`flowchart +A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in the hog"] -- "The rat in the mat" -->C;`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['A'].text).toBe('The cat in **the** hat'); + expect(vert['A'].labelType).toBe('markdown'); + expect(vert['B'].id).toBe('B'); + expect(vert['B'].text).toBe('The dog in the hog'); + expect(vert['B'].labelType).toBe('text'); + expect(edges.length).toBe(2); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe('arrow_point'); + expect(edges[0].text).toBe('The *bat* in the chat'); + expect(edges[0].labelType).toBe('markdown'); + expect(edges[1].start).toBe('B'); + expect(edges[1].end).toBe('C'); + expect(edges[1].type).toBe('arrow_point'); + expect(edges[1].text).toBe('The rat in the mat'); + expect(edges[1].labelType).toBe('text'); + }); + it('mardown formatting in subgraphs', function () { + const res = flow.parser.parse(`flowchart LR +subgraph "One" + a("\`The **cat** + in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}} +end +subgraph "\`**Two**\`" + c("\`The **cat** + in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog") +end`); + + const subgraphs = flow.parser.yy.getSubGraphs(); + expect(subgraphs.length).toBe(2); + const subgraph = subgraphs[0]; + + expect(subgraph.nodes.length).toBe(2); + expect(subgraph.title).toBe('One'); + expect(subgraph.labelType).toBe('text'); + + const subgraph2 = subgraphs[1]; + expect(subgraph2.nodes.length).toBe(2); + expect(subgraph2.title).toBe('**Two**'); + expect(subgraph2.labelType).toBe('markdown'); + }); +}); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index dd82cff4d..19ab80b05 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -38,9 +38,9 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili [\}] { this.popState(); } [^\}]* return "acc_descr_multiline_value"; // .*[^\n]* { return "acc_descr_line"} -[`] { this.begin("md_string");} -[^`]+ { return "MD_STR";} -[`] { this.popState();} +["][`] { this.begin("md_string");} +[^`"]+ { return "MD_STR";} +[`]["] { this.popState();} ["] this.begin("string"); ["] this.popState(); [^"]* return "STR"; diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.js b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.js index c5b5fede1..a2a4def59 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.js +++ b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.js @@ -167,12 +167,15 @@ function positionNodes(cy) { export const draw = async (text, id, version, diagObj) => { const conf = getConfig(); + // console.log('Config: ', conf); + conf.htmlLabels = false; + // This is done only for throwing the error if the text is not valid. diagObj.db.clear(); // Parse the graph definition diagObj.parser.parse(text); - log.debug('Renering info diagram\n' + text); + log.debug('Renering mindmap diagram\n' + text, diagObj); const securityLevel = getConfig().securityLevel; // Handle root and Document for when rendering in sandbox mode diff --git a/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison b/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison index 41ee33c91..84a6191cf 100644 --- a/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison +++ b/packages/mermaid/src/diagrams/mindmap/parser/mindmap.jison @@ -42,9 +42,9 @@ // !(-\() return 'NODE_ID'; [^\(\[\n\-\)\{\}]+ return 'NODE_ID'; <> return 'EOF'; -[`] { this.begin("NSTR2");} -[^`]+ { return "NODE_DESCR";} -[`] { this.popState();} +["][`] { this.begin("NSTR2");} +[^`"]+ { return "NODE_DESCR";} +[`]["] { this.popState();} ["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");} [^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";} ["] {this.popState();} From c777f9193d1c488e74a72f9447cb657895638b6d Mon Sep 17 00:00:00 2001 From: knsv Date: Mon, 3 Apr 2023 10:35:19 +0000 Subject: [PATCH 11/11] Update docs --- docs/config/setup/modules/defaultConfig.md | 2 +- docs/config/setup/modules/mermaidAPI.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index 6493a93db..ad8f90248 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -14,7 +14,7 @@ #### Defined in -[defaultConfig.ts:2105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2105) +[defaultConfig.ts:2115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2115) --- diff --git a/docs/config/setup/modules/mermaidAPI.md b/docs/config/setup/modules/mermaidAPI.md index 91ee30e2b..c09402dbd 100644 --- a/docs/config/setup/modules/mermaidAPI.md +++ b/docs/config/setup/modules/mermaidAPI.md @@ -96,7 +96,7 @@ mermaid.initialize(config); #### Defined in -[mermaidAPI.ts:669](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L669) +[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667) ## Functions