Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💄 section width now covers all tasks - Timeline #4126

Merged
merged 6 commits into from
Feb 28, 2023
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
2 changes: 2 additions & 0 deletions packages/mermaid/src/config.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
leftMargin?: number;
width?: number;
height?: number;
padding?: number;
boxMargin?: number;
boxTextMargin?: number;
noteMargin?: number;
Expand All @@ -311,6 +312,7 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
sectionFills?: string[];
sectionColours?: string[];
disableMulticolor?: boolean;
useMaxWidth?: boolean;
}

export interface GanttDiagramConfig extends BaseDiagramConfig {
Expand Down
45 changes: 45 additions & 0 deletions packages/mermaid/src/diagram-api/diagram-orchestration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { it, describe, expect } from 'vitest';
import { detectType } from './detectType';
import { addDiagrams } from './diagram-orchestration';

describe('diagram-orchestration', () => {
it('should register diagrams', () => {
expect(() => detectType('graph TD; A-->B')).toThrow();
addDiagrams();
expect(detectType('graph TD; A-->B')).toBe('flowchart');
});

describe('proper diagram types should be detetced', () => {
beforeAll(() => {
addDiagrams();
});

it.each([
{ text: 'graph TD;', expected: 'flowchart' },
{ text: 'flowchart TD;', expected: 'flowchart-v2' },
{ text: 'flowchart-v2 TD;', expected: 'flowchart-v2' },
{ text: 'flowchart-elk TD;', expected: 'flowchart-elk' },
{ text: 'error', expected: 'error' },
{ text: 'C4Context;', expected: 'c4' },
{ text: 'classDiagram', expected: 'class' },
{ text: 'classDiagram-v2', expected: 'classDiagram' },
{ text: 'erDiagram', expected: 'er' },
{ text: 'journey', expected: 'journey' },
{ text: 'gantt', expected: 'gantt' },
{ text: 'pie', expected: 'pie' },
{ text: 'requirementDiagram', expected: 'requirement' },
{ text: 'info', expected: 'info' },
{ text: 'sequenceDiagram', expected: 'sequence' },
{ text: 'mindmap', expected: 'mindmap' },
{ text: 'timeline', expected: 'timeline' },
{ text: 'gitGraph', expected: 'gitGraph' },
{ text: 'stateDiagram', expected: 'state' },
{ text: 'stateDiagram-v2', expected: 'stateDiagram' },
])(
'should $text be detected as $expected',
({ text, expected }: { text: string; expected: string }) => {
expect(detectType(text)).toBe(expected);
}
);
});
});
11 changes: 7 additions & 4 deletions packages/mermaid/src/diagram-api/diagram-orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const addDiagrams = () => {
throw new Error(
'Diagrams beginning with --- are not valid. ' +
'If you were trying to use a YAML front-matter, please ensure that ' +
"you've correctly opened and closed the YAML front-matter with unindented `---` blocks"
"you've correctly opened and closed the YAML front-matter with un-indented `---` blocks"
);
},
},
Expand All @@ -55,25 +55,28 @@ export const addDiagrams = () => {
return text.toLowerCase().trimStart().startsWith('---');
}
);
// Ordering of detectors is important. The first one to return true will be used.
registerLazyLoadedDiagrams(
error,
c4,
classDiagram,
classDiagramV2,
classDiagram,
er,
gantt,
info,
pie,
requirement,
sequence,
flowchartElk,
// TODO @knsv: Should v2 come before flowchart?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine. The legacy detector only triggers on graph and if the default renderer has not been configured to be dagre-wrapper this one, (flowchart), should kick in.

// This will fail few unit tests as they expect graph to be detected as flowchart, but it is detected as flowchart-v2.
flowchart,
flowchartV2,
flowchartElk,
mindmap,
timeline,
git,
state,
stateV2,
state,
journey
);
};
117 changes: 73 additions & 44 deletions packages/mermaid/src/diagrams/timeline/timelineRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
// @ts-nocheck TODO: fix file
import { select } from 'd3';
// @ts-ignore - db not typed yet
import { select, Selection } from 'd3';
import svgDraw from './svgDraw';
import { log } from '../../logger';
import { getConfig } from '../../config';
import { setupGraphViewbox } from '../../setupGraphViewbox';

export const setConf = function (cnf) {
const keys = Object.keys(cnf);

keys.forEach(function (key) {
conf[key] = cnf[key];
});
};

export const draw = function (text, id, version, diagObj) {
import { Diagram } from '../../Diagram';
import { MermaidConfig } from '../../config.type';

interface Block<TDesc, TSection> {
number: number;
descr: TDesc;
section: TSection;
width: number;
padding: number;
maxHeight: number;
}

interface TimelineTask {
id: number;
section: string;
type: string;
task: string;
score: number;
events: string[];
}
export const draw = function (text: string, id: string, version: string, diagObj: Diagram) {
//1. Fetch the configuration
const conf = getConfig();
const LEFT_MARGIN = conf.leftMargin ? conf.leftMargin : 50;
// @ts-expect-error - wrong config?
const LEFT_MARGIN = conf.leftMargin ?? 50;

//2. Clear the diagram db before parsing
diagObj.db.clear();
diagObj.db.clear?.();

//3. Parse the diagram text
diagObj.parser.parse(text + '\n');
Expand All @@ -34,23 +46,28 @@ export const draw = function (text, id, version, diagObj) {
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
? // @ts-ignore d3 types are wrong
select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');

// @ts-ignore d3 types are wrong
const svg = root.select('#' + id);

svg.append('g');

//4. Fetch the diagram data
const tasks = diagObj.db.getTasks();
// @ts-expect-error - db not typed yet
const tasks: TimelineTask[] = diagObj.db.getTasks();
// @ts-expect-error - db not typed yet
const title = diagObj.db.getCommonDb().getDiagramTitle();
log.debug('task', tasks);

//5. Initialize the diagram
svgDraw.initGraphics(svg);

// fetch Sections
const sections = diagObj.db.getSections();
// @ts-expect-error - db not typed yet
const sections: string[] = diagObj.db.getSections();
log.debug('sections', sections);

let maxSectionHeight = 0;
Expand All @@ -67,8 +84,8 @@ export const draw = function (text, id, version, diagObj) {
let hasSections = true;

//Calculate the max height of the sections
sections.forEach(function (section) {
const sectionNode = {
sections.forEach(function (section: string) {
const sectionNode: Block<string, number> = {
number: sectionNumber,
descr: section,
section: sectionNumber,
Expand All @@ -87,8 +104,9 @@ export const draw = function (text, id, version, diagObj) {
log.debug('tasks.length', tasks.length);
//calculate max task height
// for loop till tasks.length

for (const [i, task] of tasks.entries()) {
const taskNode = {
const taskNode: Block<TimelineTask, string> = {
number: i,
descr: task,
section: task.section,
Expand Down Expand Up @@ -124,11 +142,14 @@ export const draw = function (text, id, version, diagObj) {

if (sections && sections.length > 0) {
sections.forEach((section) => {
const sectionNode = {
//filter task where tasks.section == section
const tasksForSection = tasks.filter((task) => task.section === section);

const sectionNode: Block<string, number> = {
number: sectionNumber,
descr: section,
section: sectionNumber,
width: 150,
width: 200 * Math.max(tasksForSection.length, 1) - 50,
padding: 20,
maxHeight: maxSectionHeight,
};
Expand All @@ -142,8 +163,6 @@ export const draw = function (text, id, version, diagObj) {
masterY += maxSectionHeight + 50;

//draw tasks for this section
//filter task where tasks.section == section
const tasksForSection = tasks.filter((task) => task.section === section);
if (tasksForSection.length > 0) {
drawTasks(
svg,
Expand Down Expand Up @@ -215,25 +234,25 @@ export const draw = function (text, id, version, diagObj) {
setupGraphViewbox(
undefined,
svg,
conf.timeline.padding ? conf.timeline.padding : 50,
conf.timeline.useMaxWidth ? conf.timeline.useMaxWidth : false
conf.timeline?.padding ?? 50,
conf.timeline?.useMaxWidth ?? false
);

// addSVGAccessibilityFields(diagObj.db, diagram, id);
};

export const drawTasks = function (
diagram,
tasks,
sectionColor,
masterX,
masterY,
maxTaskHeight,
conf,
maxEventCount,
maxEventLineLength,
maxSectionHeight,
isWithoutSections
diagram: Selection<SVGElement, unknown, null, undefined>,
tasks: TimelineTask[],
sectionColor: number,
masterX: number,
masterY: number,
maxTaskHeight: number,
conf: MermaidConfig,
maxEventCount: number,
maxEventLineLength: number,
maxSectionHeight: number,
isWithoutSections: boolean
) {
// Draw the tasks
for (const task of tasks) {
Expand All @@ -249,6 +268,7 @@ export const drawTasks = function (

log.debug('taskNode', taskNode);
// create task wrapper

const taskWrapper = diagram.append('g').attr('class', 'taskWrapper');
const node = svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
const taskHeight = node.height;
Expand All @@ -263,11 +283,11 @@ export const drawTasks = function (
if (task.events) {
// draw a line between the task and the events
const lineWrapper = diagram.append('g').attr('class', 'lineWrapper');
let linelength = maxTaskHeight;
let lineLength = maxTaskHeight;
//add margin to task
masterY += 100;
linelength =
linelength + drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
lineLength =
lineLength + drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
masterY -= 100;

lineWrapper
Expand All @@ -290,7 +310,7 @@ export const drawTasks = function (
}

masterX = masterX + 200;
if (isWithoutSections && !getConfig().timeline.disableMulticolor) {
if (isWithoutSections && !conf.timeline?.disableMulticolor) {
sectionColor++;
}
}
Expand All @@ -299,14 +319,21 @@ export const drawTasks = function (
masterY = masterY - 10;
};

export const drawEvents = function (diagram, events, sectionColor, masterX, masterY, conf) {
export const drawEvents = function (
diagram: Selection<SVGElement, unknown, null, undefined>,
events: string[],
sectionColor: number,
masterX: number,
masterY: number,
conf: MermaidConfig
) {
let maxEventHeight = 0;
const eventBeginY = masterY;
masterY = masterY + 100;
// Draw the events
for (const event of events) {
// create node from event
const eventNode = {
const eventNode: Block<string, number> = {
descr: event,
section: sectionColor,
number: sectionColor,
Expand All @@ -331,6 +358,8 @@ export const drawEvents = function (diagram, events, sectionColor, masterX, mast
};

export default {
setConf,
setConf: () => {
// no-op
},
draw,
};
2 changes: 1 addition & 1 deletion packages/mermaid/src/mermaidAPI.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ describe('mermaidAPI', () => {
).rejects.toThrow(
'Diagrams beginning with --- are not valid. ' +
'If you were trying to use a YAML front-matter, please ensure that ' +
"you've correctly opened and closed the YAML front-matter with unindented `---` blocks"
"you've correctly opened and closed the YAML front-matter with un-indented `---` blocks"
);
});
it('does not throw for a valid definition', async () => {
Expand Down