diff --git a/.changeset/gold-ducks-sort.md b/.changeset/gold-ducks-sort.md new file mode 100644 index 000000000..b515eeaf8 --- /dev/null +++ b/.changeset/gold-ducks-sort.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Proper separation between strings and markdown strings diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index 5ef32c269..1ebf83a0b 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -1174,8 +1174,8 @@ end end githost["Github, Gitlab, BitBucket, etc."] githost2["\`Github, Gitlab, BitBucket, etc.\`"] - a["1."] - b["- x"] + a["\`1.\`"] + b["\`- x\`"] `; it('should render raw strings', () => { diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 40713ac4e..f94ca7d4e 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -973,4 +973,49 @@ graph TD } ); }); + it('#5824: should be able to render string and markdown labels', () => { + imgSnapshotTest( + ` +flowchart TB + mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"] + mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"] + subgraph subgraph1["\`How to fix **fix**\`"] + broken --> B["B"] + end + githost["Github, Gitlab, BitBucket, etc."] + githost2["\`Github, Gitlab, BitBucket, etc.\`"] + a["1."] + b["- x"] + `, + { + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('69: should render subgraphs with adhoc list headings', () => { + imgSnapshotTest( + ` + graph TB + subgraph "1. first" + a1-->a2 + end + subgraph 2. second + b1-->b2 + end + `, + { fontFamily: 'courier' } + ); + }); + it('70: should render subgraphs with markdown headings', () => { + imgSnapshotTest( + ` + graph TB + subgraph "\`**strong**\`" + a1-->a2 + end + `, + { fontFamily: 'courier' } + ); + }); }); diff --git a/docs/config/setup/mermaid/interfaces/ParseOptions.md b/docs/config/setup/mermaid/interfaces/ParseOptions.md index 628da0da0..185a78e8e 100644 --- a/docs/config/setup/mermaid/interfaces/ParseOptions.md +++ b/docs/config/setup/mermaid/interfaces/ParseOptions.md @@ -10,7 +10,7 @@ # Interface: ParseOptions -Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88) +Defined in: [packages/mermaid/src/types.ts:73](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L73) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mer > `optional` **suppressErrors**: `boolean` -Defined in: [packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93) +Defined in: [packages/mermaid/src/types.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L78) If `true`, parse will return `false` instead of throwing error when the diagram is invalid. The `parseError` function will not be called. diff --git a/docs/config/setup/mermaid/interfaces/ParseResult.md b/docs/config/setup/mermaid/interfaces/ParseResult.md index 0e200aa95..1ceb870c3 100644 --- a/docs/config/setup/mermaid/interfaces/ParseResult.md +++ b/docs/config/setup/mermaid/interfaces/ParseResult.md @@ -10,7 +10,7 @@ # Interface: ParseResult -Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L96) +Defined in: [packages/mermaid/src/types.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L81) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mer > **config**: [`MermaidConfig`](MermaidConfig.md) -Defined in: [packages/mermaid/src/types.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L104) +Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89) The config passed as YAML frontmatter or directives @@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives > **diagramType**: `string` -Defined in: [packages/mermaid/src/types.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L100) +Defined in: [packages/mermaid/src/types.ts:85](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L85) The diagram type, e.g. 'flowchart', 'sequence', etc. diff --git a/docs/config/setup/mermaid/interfaces/RenderResult.md b/docs/config/setup/mermaid/interfaces/RenderResult.md index 237c51de2..7cb80526b 100644 --- a/docs/config/setup/mermaid/interfaces/RenderResult.md +++ b/docs/config/setup/mermaid/interfaces/RenderResult.md @@ -10,7 +10,7 @@ # Interface: RenderResult -Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L114) +Defined in: [packages/mermaid/src/types.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L99) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/me > `optional` **bindFunctions**: (`element`) => `void` -Defined in: [packages/mermaid/src/types.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L132) +Defined in: [packages/mermaid/src/types.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L117) Bind function to be called after the svg has been inserted into the DOM. This is necessary for adding event listeners to the elements in the svg. @@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present. > **diagramType**: `string` -Defined in: [packages/mermaid/src/types.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L122) +Defined in: [packages/mermaid/src/types.ts:107](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L107) The diagram type, e.g. 'flowchart', 'sequence', etc. @@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc. > **svg**: `string` -Defined in: [packages/mermaid/src/types.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L118) +Defined in: [packages/mermaid/src/types.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L103) The svg code for the rendered graph. diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts index 632633730..2329f0ace 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -85,6 +85,17 @@ export class FlowDB implements DiagramDB { return common.sanitizeText(txt, this.config); } + private sanitizeNodeLabelType(labelType?: string) { + switch (labelType) { + case 'markdown': + case 'string': + case 'text': + return labelType; + default: + return 'markdown'; + } + } + /** * Function to lookup domId from id in the graph definition. * @@ -208,6 +219,7 @@ export class FlowDB implements DiagramDB { if (doc?.label) { vertex.text = doc?.label; + vertex.labelType = this.sanitizeNodeLabelType(doc?.labelType); } if (doc?.icon) { vertex.icon = doc?.icon; @@ -267,7 +279,7 @@ export class FlowDB implements DiagramDB { if (edge.text.startsWith('"') && edge.text.endsWith('"')) { edge.text = edge.text.substring(1, edge.text.length - 1); } - edge.labelType = linkTextObj.type; + edge.labelType = this.sanitizeNodeLabelType(linkTextObj.type); } if (type !== undefined) { @@ -702,7 +714,7 @@ You have to call mermaid.initialize.` title: title.trim(), classes: [], dir, - labelType: _title.type, + labelType: this.sanitizeNodeLabelType(_title?.type), }; log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir); @@ -1012,6 +1024,7 @@ You have to call mermaid.initialize.` const baseNode = { id: vertex.id, label: vertex.text, + labelType: vertex.labelType, labelStyle: '', parentId, padding: config.flowchart?.padding || 8, @@ -1088,6 +1101,7 @@ You have to call mermaid.initialize.` id: subGraph.id, label: subGraph.title, labelStyle: '', + labelType: subGraph.labelType, parentId: parentDB.get(subGraph.id), padding: 8, cssCompiledStyles: this.getCompiledStyles(subGraph.classes), @@ -1119,6 +1133,7 @@ You have to call mermaid.initialize.` end: rawEdge.end, type: rawEdge.type ?? 'normal', label: rawEdge.text, + labelType: rawEdge.labelType, labelpos: 'c', thickness: rawEdge.stroke, minlen: rawEdge.length, diff --git a/packages/mermaid/src/diagrams/flowchart/types.ts b/packages/mermaid/src/diagrams/flowchart/types.ts index 54156091b..ac2d04015 100644 --- a/packages/mermaid/src/diagrams/flowchart/types.ts +++ b/packages/mermaid/src/diagrams/flowchart/types.ts @@ -29,7 +29,7 @@ export interface FlowVertex { domId: string; haveCallback?: boolean; id: string; - labelType: 'text'; + labelType: 'markdown' | 'string' | 'text'; link?: string; linkTarget?: string; props?: any; @@ -62,7 +62,7 @@ export interface FlowEdge { style?: string[]; length?: number; text: string; - labelType: 'text'; + labelType: 'markdown' | 'string' | 'text'; classes: string[]; id?: string; animation?: 'fast' | 'slow'; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js index 1dd87d438..4f49bd5d6 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js @@ -30,11 +30,17 @@ const rect = async (parent, node) => { // Create the label and insert it after the rect const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label '); - const text = await createText(labelEl, node.label, { - style: node.labelStyle, - useHtmlLabels, - isNode: true, - }); + let text; + if (node.labelType === 'markdown') { + text = await createText(labelEl, node.label, { + style: node.labelStyle, + useHtmlLabels, + isNode: true, + }); + } else { + const labelElement = await createLabel(node.label, node.labelStyle, false, true); + text = labelEl.node()?.appendChild(labelElement); + } // Get the size of the label let bbox = text.getBBox(); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js index 9e308631a..c759bc127 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js @@ -29,7 +29,7 @@ import { import rough from 'roughjs'; import createLabel from './createLabel.js'; import { addEdgeMarkers } from './edgeMarker.ts'; -import { isLabelStyle, styles2String } from './shapes/handDrawnShapeStyles.js'; +import { isLabelStyle } from './shapes/handDrawnShapeStyles.js'; export const edgeLabels = new Map(); export const terminalLabels = new Map(); @@ -47,14 +47,16 @@ export const getLabelStyles = (styleArray) => { export const insertEdgeLabel = async (elem, edge) => { let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels); - const { labelStyles } = styles2String(edge); - edge.labelStyle = labelStyles; - const labelElement = await createText(elem, edge.label, { - style: edge.labelStyle, - useHtmlLabels, - addSvgBackground: true, - isNode: false, - }); + const labelElement = + edge.labelType === 'markdown' + ? await createText(elem, edge.label, { + style: getLabelStyles(edge.labelStyle), + useHtmlLabels, + addSvgBackground: true, + isNode: false, + }) + : await createLabel(edge.label, getLabelStyles(edge.labelStyle), undefined, false); + log.info('abc82', edge, edge.labelType); // Create outer g, edgeLabel, this will be positioned after graph layout diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts index 52471ecc0..5911f8f34 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts @@ -1,3 +1,4 @@ +import createLabel from '../createLabel.js'; import { createText } from '../../createText.js'; import type { Node } from '../../types.js'; import { getConfig } from '../../../diagram-api/diagramAPI.js'; @@ -13,7 +14,7 @@ export const labelHelper = async ( _classes?: string ) => { let cssClasses; - const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.htmlLabels); + const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels); if (!_classes) { cssClasses = 'node default'; } else { @@ -40,14 +41,26 @@ export const labelHelper = async ( label = typeof node.label === 'string' ? node.label : node.label[0]; } - const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), { - useHtmlLabels, - width: node.width || getConfig().flowchart?.wrappingWidth, - // @ts-expect-error -- This is currently not used. Should this be `classes` instead? - cssClasses: 'markdown-node-label', - style: node.labelStyle, - addSvgBackground: !!node.icon || !!node.img, - }); + let text; + if (node.labelType === 'markdown') { + text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), { + useHtmlLabels, + width: node.width || getConfig().flowchart?.wrappingWidth, + // @ts-expect-error -- This is currently not used. Should this be `classes` instead? + cssClasses: 'markdown-node-label', + style: node.labelStyle, + addSvgBackground: !!node.icon || !!node.img, + }); + } else { + const labelElement = await createLabel( + sanitizeText(decodeEntities(label), getConfig()), + node.labelStyle, + false, + true + ); + text = labelEl.node()?.appendChild(labelElement); + } + // Get the size of the label let bbox = text.getBBox(); const halfPadding = (node?.padding ?? 0) / 2; diff --git a/packages/mermaid/src/types.ts b/packages/mermaid/src/types.ts index 727b6bb3a..31b8e29f6 100644 --- a/packages/mermaid/src/types.ts +++ b/packages/mermaid/src/types.ts @@ -1,6 +1,7 @@ export interface NodeMetaData { shape?: string; label?: string; + labelType?: string; icon?: string; form?: string; pos?: 't' | 'b';