Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-block-runtime-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': patch
---

fix: read block padding and sanitize config dynamically instead of at module load time
19 changes: 19 additions & 0 deletions packages/mermaid/src/diagrams/block/blockDB.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import * as diagramAPI from '../../diagram-api/diagramAPI.js';
import db from './blockDB.js';

describe('block db edge styles', () => {
Expand Down Expand Up @@ -27,3 +29,20 @@ describe('block db edge styles', () => {
expect(db.edgeStrToEdgeData('--o')).toBe('arrow_circle');
});
});

describe('block db runtime config', () => {
afterEach(() => {
vi.restoreAllMocks();
db.clear();
});

it('should call getConfig at sanitization time, not at module load time', () => {
const spy = vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({} as any);

db.setHierarchy([{ id: 'a', type: 'square', label: 'hello', children: [] }]);

// getConfig must have been called during setHierarchy (call-time read),
// not only once at module import time.
expect(spy).toHaveBeenCalled();
});
});
4 changes: 1 addition & 3 deletions packages/mermaid/src/diagrams/block/blockDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ const COLOR_KEYWORD = 'color';
const FILL_KEYWORD = 'fill';
const BG_FILL = 'bgFill';
const STYLECLASS_SEP = ',';
const config = getConfig();

let classes = new Map<string, ClassDef>();
let diagramId = '';

const sanitizeText = (txt: string) => common.sanitizeText(txt, config);
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());

/**
* Called when the parser comes across a (style) class definition
Expand Down
39 changes: 38 additions & 1 deletion packages/mermaid/src/diagrams/block/layout.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { calculateBlockPosition } from './layout.js';
import { describe, it, expect, vi, afterEach } from 'vitest';
import * as diagramAPI from '../../diagram-api/diagramAPI.js';
import type { BlockDB } from './blockDB.js';
import type { Block } from './blockTypes.js';
import { calculateBlockPosition, layout } from './layout.js';

describe('Layout', function () {
it('should calculate position correctly', () => {
Expand All @@ -10,3 +14,36 @@ describe('Layout', function () {
expect(calculateBlockPosition(1, 3)).toEqual({ px: 0, py: 3 });
});
});

describe('layout runtime config', () => {
afterEach(() => {
vi.restoreAllMocks();
});

it('should read block.padding from config at call time, not at import time', () => {
const makeRoot = (): Block => ({
id: 'root',
type: 'square',
columns: 2,
children: [
{ id: 'b1', type: 'square', children: [], size: { width: 100, height: 50, x: 0, y: 0 } },
{ id: 'b2', type: 'square', children: [], size: { width: 100, height: 50, x: 0, y: 0 } },
],
});

const makeDb = (root: Block): BlockDB =>
({ getBlock: (id: string) => (id === 'root' ? root : undefined) }) as unknown as BlockDB;

vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({ block: { padding: 4 } } as any);
const result1 = layout(makeDb(makeRoot()));

vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({ block: { padding: 20 } } as any);
const result2 = layout(makeDb(makeRoot()));

// padding=4: width = 2*(100+4)+4 = 212
// padding=20: width = 2*(100+20)+20 = 260
// If padding were cached at module load time both calls would return the same value.
expect(result1).not.toEqual(result2);
expect(result1!.width).toBeLessThan(result2!.width);
});
});
23 changes: 14 additions & 9 deletions packages/mermaid/src/diagrams/block/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import type { BlockDB } from './blockDB.js';
import type { Block } from './blockTypes.js';
import { log } from '../../logger.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
// TODO: This means the number we provide in diagram's config will never be used. Should fix.
const padding = getConfig()?.block?.padding ?? 8;

interface BlockPosition {
px: number;
Expand Down Expand Up @@ -71,7 +69,13 @@ const getMaxChildSize = (block: Block) => {
return { width: maxWidth, height: maxHeight };
};

function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeight = 0) {
function setBlockSizes(
block: Block,
db: BlockDB,
siblingWidth = 0,
siblingHeight = 0,
padding = 8
) {
log.debug(
'setBlockSizes abc95 (start)',
block.id,
Expand All @@ -94,7 +98,7 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh

if (block.children?.length > 0) {
for (const child of block.children) {
setBlockSizes(child, db);
setBlockSizes(child, db, 0, 0, padding);
}
// find max width of children
const childSize = getMaxChildSize(block);
Expand All @@ -120,7 +124,7 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh
}
}
for (const child of block.children) {
setBlockSizes(child, db, maxWidth, maxHeight);
setBlockSizes(child, db, maxWidth, maxHeight, padding);
}

const columns = block.columns ?? -1;
Expand Down Expand Up @@ -202,7 +206,7 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh
);
}

function layoutBlocks(block: Block, db: BlockDB) {
function layoutBlocks(block: Block, db: BlockDB, padding = 8) {
log.debug(
`abc85 layout blocks (=>layoutBlocks) ${block.id} x: ${block?.size?.x} y: ${block?.size?.y} width: ${block?.size?.width}`
);
Expand Down Expand Up @@ -301,7 +305,7 @@ function layoutBlocks(block: Block, db: BlockDB) {
);
}
if (child.children) {
layoutBlocks(child, db);
layoutBlocks(child, db, padding);
}
let columnsFilled = child?.widthInColumns ?? 1;
if (columns > 0) {
Expand Down Expand Up @@ -350,8 +354,9 @@ export function layout(db: BlockDB) {
return;
}

setBlockSizes(root, db, 0, 0);
layoutBlocks(root, db);
const padding = getConfig()?.block?.padding ?? 8;
setBlockSizes(root, db, 0, 0, padding);
layoutBlocks(root, db, padding);
// Position blocks relative to parents
// positionBlock(root, root, db);
log.debug('getBlocks', JSON.stringify(root, null, 2));
Expand Down
19 changes: 19 additions & 0 deletions packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import * as diagramAPI from '../../diagram-api/diagramAPI.js';
import quadrantDb from './quadrantDb.js';

describe('quadrant unit tests', () => {
Expand Down Expand Up @@ -48,3 +50,20 @@ describe('quadrant unit tests', () => {
);
});
});

describe('quadrant db runtime config', () => {
afterEach(() => {
vi.restoreAllMocks();
quadrantDb.clear();
});

it('should call getConfig at sanitization time, not at module load time', () => {
const spy = vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({} as any);

quadrantDb.setXAxisLeftText({ text: 'left label', type: 'text' });

// getConfig must have been called during setXAxisLeftText (call-time read),
// not only once at module import time.
expect(spy).toHaveBeenCalled();
});
});
4 changes: 1 addition & 3 deletions packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ import {
InvalidStyleError,
} from './utils.js';

const config = getConfig();

function textSanitizer(text: string) {
return sanitizeText(text.trim(), config);
return sanitizeText(text.trim(), getConfig());
}

interface LexTextObj {
Expand Down
Loading