Skip to content

Commit

Permalink
Merge pull request #4856 from dreathed/bug/4391_make_markdown_auto_wr…
Browse files Browse the repository at this point in the history
…apping_optional

Bug/4391 make markdown auto wrapping optional
  • Loading branch information
sidharthv96 authored Mar 23, 2024
2 parents ea86697 + 16db0c0 commit 223f339
Show file tree
Hide file tree
Showing 16 changed files with 134 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

NODE_OPTIONS=--max_old_space_size=8192 pnpm run pre-commit
NODE_OPTIONS="--max_old_space_size=8192" pnpm run pre-commit
12 changes: 12 additions & 0 deletions cypress/integration/rendering/flowchart-v2.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,18 @@ end
);
});
});

it('should not auto wrap when markdownAutoWrap is false', () => {
imgSnapshotTest(
`flowchart TD
angular_velocity["\`**angular_velocity**
*angular_displacement / duration*
[rad/s, 1/s]
{vector}\`"]
frequency["frequency\n(1 / period_duration)\n[Hz, 1/s]"]`,
{ markdownAutoWrap: false }
);
});
});
describe('Subgraph title margins', () => {
it('Should render subgraphs with title margins set (LR)', () => {
Expand Down
2 changes: 1 addition & 1 deletion docs/config/setup/modules/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Pushes in a directive to the configuration
| --------- | ------------------------- | ----------- | ------------------------------ |
| getConfig | Obtains the currentConfig | Get Request | Any Values from current Config |

**Notes**: Returns **any** the currentConfig
**Notes**: Avoid calling this function repeatedly. Instead, store the result in a variable and use it, and pass it down to function calls.

#### Returns

Expand Down
10 changes: 10 additions & 0 deletions docs/syntax/flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,16 @@ Formatting:

This feature is applicable to node labels, edge labels, and subgraph labels.

The auto wrapping can be disabled by using

```
---
config:
markdownAutoWrap: false
---
graph LR
```

## Interaction

It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab.
Expand Down
2 changes: 1 addition & 1 deletion packages/mermaid/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const setConfig = (conf: MermaidConfig): MermaidConfig => {
* | --------- | ------------------------- | ----------- | ------------------------------ |
* | getConfig | Obtains the currentConfig | Get Request | Any Values from current Config |
*
* **Notes**: Returns **any** the currentConfig
* **Notes**: Avoid calling this function repeatedly. Instead, store the result in a variable and use it, and pass it down to function calls.
*
* @returns The currentConfig
*/
Expand Down
1 change: 1 addition & 0 deletions packages/mermaid/src/config.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export interface MermaidConfig {
dompurifyConfig?: DOMPurifyConfiguration;
wrap?: boolean;
fontSize?: number;
markdownAutoWrap?: boolean;
/**
* Suppresses inserting 'Syntax error' diagram in the DOM.
* This is useful when you want to control how to handle syntax errors in your application.
Expand Down
2 changes: 1 addition & 1 deletion packages/mermaid/src/dagre-wrapper/clusters.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const rect = (parent, node) => {
// .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
const text =
node.labelType === 'markdown'
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels })
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels }, siteConfig)
: label.node().appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));

// Get the size of the label
Expand Down
18 changes: 12 additions & 6 deletions packages/mermaid/src/dagre-wrapper/edges.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,21 @@ export const clear = () => {
};

export const insertEdgeLabel = (elem, edge) => {
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
const config = getConfig();
const useHtmlLabels = evaluate(config.flowchart.htmlLabels);
// Create the actual text element
const labelElement =
edge.labelType === 'markdown'
? createText(elem, edge.label, {
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
})
? createText(
elem,
edge.label,
{
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
},
config
)
: createLabel(edge.label, edge.labelStyle);

// Create outer g, edgeLabel, this will be positioned after graph layout
Expand Down
31 changes: 16 additions & 15 deletions packages/mermaid/src/dagre-wrapper/shapes/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { evaluate, sanitizeText } from '../../diagrams/common/common.js';
import { decodeEntities } from '../../utils.js';

export const labelHelper = async (parent, node, _classes, isNode) => {
const config = getConfig();
let classes;
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
const useHtmlLabels = node.useHtmlLabels || evaluate(config.flowchart.htmlLabels);
if (!_classes) {
classes = 'node default';
} else {
Expand Down Expand Up @@ -35,26 +36,26 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
let text;
if (node.labelType === 'markdown') {
// text = textNode;
text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
useHtmlLabels,
width: node.width || getConfig().flowchart.wrappingWidth,
classes: 'markdown-node-label',
});
text = createText(
label,
sanitizeText(decodeEntities(labelText), config),
{
useHtmlLabels,
width: node.width || config.flowchart.wrappingWidth,
classes: 'markdown-node-label',
},
config
);
} else {
text = textNode.appendChild(
createLabel(
sanitizeText(decodeEntities(labelText), getConfig()),
node.labelStyle,
false,
isNode
)
createLabel(sanitizeText(decodeEntities(labelText), config), node.labelStyle, false, isNode)
);
}
// Get the size of the label
let bbox = text.getBBox();
const halfPadding = node.padding / 2;

if (evaluate(getConfig().flowchart.htmlLabels)) {
if (evaluate(config.flowchart.htmlLabels)) {
const div = text.children[0];
const dv = select(text);

Expand All @@ -76,8 +77,8 @@ export const labelHelper = async (parent, node, _classes, isNode) => {

if (noImgText) {
// default size if no text
const bodyFontSize = getConfig().fontSize
? getConfig().fontSize
const bodyFontSize = config.fontSize
? config.fontSize
: window.getComputedStyle(document.body).fontSize;
const enlargingFactor = 5;
const width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';
Expand Down
6 changes: 2 additions & 4 deletions packages/mermaid/src/diagrams/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,9 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
.split(lineBreakRegex)
.map((line) =>
hasKatex(line)
? `
<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
${line}
</div>
`
</div>`
: `<div>${line}</div>`
)
.join('')
Expand Down
15 changes: 10 additions & 5 deletions packages/mermaid/src/diagrams/mindmap/svgDraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,16 @@ export const drawNode = function (
// Create the wrapped text element
const textElem = nodeElem.append('g');
const description = node.descr.replace(/(<br\/*>)/g, '\n');
const newEl = createText(textElem, description, {
useHtmlLabels: htmlLabels,
width: node.width,
classes: 'mindmap-node-label',
});
const newEl = createText(
textElem,
description,
{
useHtmlLabels: htmlLabels,
width: node.width,
classes: 'mindmap-node-label',
},
conf
);

if (!htmlLabels) {
textElem
Expand Down
10 changes: 10 additions & 0 deletions packages/mermaid/src/docs/syntax/flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,16 @@ Formatting:

This feature is applicable to node labels, edge labels, and subgraph labels.

The auto wrapping can be disabled by using

```
---
config:
markdownAutoWrap: false
---
graph LR
```

## Interaction

It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab.
Expand Down
14 changes: 7 additions & 7 deletions packages/mermaid/src/rendering-util/createText.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck TODO: Fix types
import type { MermaidConfig } from '../config.type.js';
import type { Group } from '../diagram-api/types.js';
import type { D3TSpanElement, D3TextElement } from '../diagrams/common/commonTypes.js';
import { log } from '../logger.js';
Expand All @@ -21,8 +22,7 @@ function addHtmlSpan(element, node, width, classes, addBackground = false) {
const label = node.label;
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
div.html(
`
<span class="${labelClass} ${classes}" ` +
`<span class="${labelClass} ${classes}" ` +
(node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
'>' +
label +
Expand Down Expand Up @@ -181,14 +181,14 @@ export const createText = (
isNode = true,
width = 200,
addSvgBackground = false,
} = {}
} = {},
config: MermaidConfig
) => {
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, '<br />');
const htmlText = markdownToHTML(text);
// log.info('markdownToHTML' + text, markdownToHTML(text));

const htmlText = markdownToHTML(text, config);
const node = {
isNode,
label: decodeEntities(htmlText).replace(
Expand All @@ -200,7 +200,7 @@ export const createText = (
const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
return vertexNode;
} else {
const structuredText = markdownToLines(text);
const structuredText = markdownToLines(text, config);
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
return svgLabel;
}
Expand Down
36 changes: 36 additions & 0 deletions packages/mermaid/src/rendering-util/handle-markdown-text.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-irregular-whitespace */
import { markdownToLines, markdownToHTML } from './handle-markdown-text.js';
import { test, expect } from 'vitest';

Expand Down Expand Up @@ -203,6 +204,31 @@ Word!`;
expect(output).toEqual(expectedOutput);
});

test('markdownToLines - No auto wrapping', () => {
expect(
markdownToLines(
`Hello, how do
you do?`,
{ markdownAutoWrap: false }
)
).toMatchInlineSnapshot(`
[
[
{
"content": "Hello, how do",
"type": "normal",
},
],
[
{
"content": "you do?",
"type": "normal",
},
],
]
`);
});

test('markdownToHTML - Basic test', () => {
const input = `This is regular text
Here is a new line
Expand Down Expand Up @@ -262,3 +288,13 @@ test('markdownToHTML - Unsupported formatting', () => {
- l3`)
).toMatchInlineSnapshot('"<p>Hello</p>Unsupported markdown: list"');
});

test('markdownToHTML - no auto wrapping', () => {
expect(
markdownToHTML(
`Hello, how do
you do?`,
{ markdownAutoWrap: false }
)
).toMatchInlineSnapshot('"<p>Hello,&nbsp;how&nbsp;do<br/>you&nbsp;do?</p>"');
});
15 changes: 11 additions & 4 deletions packages/mermaid/src/rendering-util/handle-markdown-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@ import type { Content } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { dedent } from 'ts-dedent';
import type { MarkdownLine, MarkdownWordType } from './types.js';
import type { MermaidConfig } from '../config.type.js';

/**
* @param markdown - markdown to process
* @returns processed markdown
*/
function preprocessMarkdown(markdown: string): string {
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string {
// 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 = dedent(withoutMultipleNewlines);
if (markdownAutoWrap === false) {
return withoutExtraSpaces.replace(/ /g, '&nbsp;');
}
return withoutExtraSpaces;
}

/**
* @param markdown - markdown to split into lines
*/
export function markdownToLines(markdown: string): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown);
export function markdownToLines(markdown: string, config: MermaidConfig = {}): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown, config);
const { children } = fromMarkdown(preprocessedMarkdown);
const lines: MarkdownLine[] = [[]];
let currentLine = 0;
Expand Down Expand Up @@ -56,11 +60,14 @@ export function markdownToLines(markdown: string): MarkdownLine[] {
return lines;
}

export function markdownToHTML(markdown: string) {
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidConfig = {}) {
const { children } = fromMarkdown(markdown);

function output(node: Content): string {
if (node.type === 'text') {
if (markdownAutoWrap === false) {
return node.value.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;');
}
return node.value.replace(/\n/g, '<br/>');
} else if (node.type === 'strong') {
return `<strong>${node.children.map(output).join('')}</strong>`;
Expand Down
3 changes: 3 additions & 0 deletions packages/mermaid/src/schemas/config.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ properties:
fontSize:
type: number
default: 16
markdownAutoWrap:
type: boolean
default: true
suppressErrorRendering:
type: boolean
default: false
Expand Down

0 comments on commit 223f339

Please sign in to comment.