• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mermaid-js / mermaid / 5071601472

pending completion
5071601472

push

github

Knut Sveidqvist
Merge branch 'release/10.2.0'

1633 of 2064 branches covered (79.12%)

Branch coverage included in aggregate %.

2701 of 2701 new or added lines in 128 files covered. (100.0%)

19402 of 34929 relevant lines covered (55.55%)

418.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

15.66
/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts
1
// @ts-nocheck TODO: fix file
1✔
2
import { select } from 'd3';
1✔
3
import svgDraw from './svgDraw.js';
1✔
4
import { getConfig } from '../../config.js';
1✔
5
import { configureSvgSize } from '../../setupGraphViewbox.js';
1✔
6

1✔
7
export const setConf = function (cnf) {
1✔
8
  const keys = Object.keys(cnf);
×
9

×
10
  keys.forEach(function (key) {
×
11
    conf[key] = cnf[key];
×
12
  });
×
13
};
×
14

1✔
15
const actors = {};
1✔
16

1✔
17
/** @param diagram - The diagram to draw to. */
1✔
18
function drawActorLegend(diagram) {
×
19
  const conf = getConfig().journey;
×
20
  // Draw the actors
×
21
  let yPos = 60;
×
22
  Object.keys(actors).forEach((person) => {
×
23
    const colour = actors[person].color;
×
24

×
25
    const circleData = {
×
26
      cx: 20,
×
27
      cy: yPos,
×
28
      r: 7,
×
29
      fill: colour,
×
30
      stroke: '#000',
×
31
      pos: actors[person].position,
×
32
    };
×
33
    svgDraw.drawCircle(diagram, circleData);
×
34

×
35
    const labelData = {
×
36
      x: 40,
×
37
      y: yPos + 7,
×
38
      fill: '#666',
×
39
      text: person,
×
40
      textMargin: conf.boxTextMargin | 5,
×
41
    };
×
42
    svgDraw.drawText(diagram, labelData);
×
43

×
44
    yPos += 20;
×
45
  });
×
46
}
×
47
// TODO: Cleanup?
1✔
48
const conf = getConfig().journey;
1✔
49
const LEFT_MARGIN = conf.leftMargin;
1✔
50
export const draw = function (text, id, version, diagObj) {
1✔
51
  const conf = getConfig().journey;
×
52
  diagObj.db.clear();
×
53
  diagObj.parser.parse(text + '\n');
×
54

×
55
  const securityLevel = getConfig().securityLevel;
×
56
  // Handle root and Document for when rendering in sandbox mode
×
57
  let sandboxElement;
×
58
  if (securityLevel === 'sandbox') {
×
59
    sandboxElement = select('#i' + id);
×
60
  }
×
61
  const root =
×
62
    securityLevel === 'sandbox'
×
63
      ? select(sandboxElement.nodes()[0].contentDocument.body)
×
64
      : select('body');
×
65
  // const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
×
66

×
67
  bounds.init();
×
68
  const diagram = root.select('#' + id);
×
69

×
70
  svgDraw.initGraphics(diagram);
×
71

×
72
  const tasks = diagObj.db.getTasks();
×
73
  const title = diagObj.db.getDiagramTitle();
×
74

×
75
  const actorNames = diagObj.db.getActors();
×
76
  for (const member in actors) {
×
77
    delete actors[member];
×
78
  }
×
79
  let actorPos = 0;
×
80
  actorNames.forEach((actorName) => {
×
81
    actors[actorName] = {
×
82
      color: conf.actorColours[actorPos % conf.actorColours.length],
×
83
      position: actorPos,
×
84
    };
×
85
    actorPos++;
×
86
  });
×
87

×
88
  drawActorLegend(diagram);
×
89
  bounds.insert(0, 0, LEFT_MARGIN, Object.keys(actors).length * 50);
×
90
  drawTasks(diagram, tasks, 0);
×
91

×
92
  const box = bounds.getBounds();
×
93
  if (title) {
×
94
    diagram
×
95
      .append('text')
×
96
      .text(title)
×
97
      .attr('x', LEFT_MARGIN)
×
98
      .attr('font-size', '4ex')
×
99
      .attr('font-weight', 'bold')
×
100
      .attr('y', 25);
×
101
  }
×
102

×
103
  const height = box.stopy - box.starty + 2 * conf.diagramMarginY;
×
104
  const width = LEFT_MARGIN + box.stopx + 2 * conf.diagramMarginX;
×
105

×
106
  configureSvgSize(diagram, height, width, conf.useMaxWidth);
×
107

×
108
  // Draw activity line
×
109
  diagram
×
110
    .append('line')
×
111
    .attr('x1', LEFT_MARGIN)
×
112
    .attr('y1', conf.height * 4) // One section head + one task + margins
×
113
    .attr('x2', width - LEFT_MARGIN - 4) // Subtract stroke width so arrow point is retained
×
114
    .attr('y2', conf.height * 4)
×
115
    .attr('stroke-width', 4)
×
116
    .attr('stroke', 'black')
×
117
    .attr('marker-end', 'url(#arrowhead)');
×
118

×
119
  const extraVertForTitle = title ? 70 : 0;
×
120
  diagram.attr('viewBox', `${box.startx} -25 ${width} ${height + extraVertForTitle}`);
×
121
  diagram.attr('preserveAspectRatio', 'xMinYMin meet');
×
122
  diagram.attr('height', height + extraVertForTitle + 25);
×
123
};
×
124

1✔
125
export const bounds = {
1✔
126
  data: {
1✔
127
    startx: undefined,
1✔
128
    stopx: undefined,
1✔
129
    starty: undefined,
1✔
130
    stopy: undefined,
1✔
131
  },
1✔
132
  verticalPos: 0,
1✔
133

1✔
134
  sequenceItems: [],
1✔
135
  init: function () {
1✔
136
    this.sequenceItems = [];
×
137
    this.data = {
×
138
      startx: undefined,
×
139
      stopx: undefined,
×
140
      starty: undefined,
×
141
      stopy: undefined,
×
142
    };
×
143
    this.verticalPos = 0;
×
144
  },
×
145
  updateVal: function (obj, key, val, fun) {
1✔
146
    if (obj[key] === undefined) {
×
147
      obj[key] = val;
×
148
    } else {
×
149
      obj[key] = fun(val, obj[key]);
×
150
    }
×
151
  },
×
152
  updateBounds: function (startx, starty, stopx, stopy) {
1✔
153
    const conf = getConfig().journey;
×
154
    // eslint-disable-next-line @typescript-eslint/no-this-alias
×
155
    const _self = this;
×
156
    let cnt = 0;
×
157
    /** @param type - Set to `activation` if activation */
×
158
    function updateFn(type?: 'activation') {
×
159
      return function updateItemBounds(item) {
×
160
        cnt++;
×
161
        // The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems
×
162
        const n = _self.sequenceItems.length - cnt + 1;
×
163
        _self.updateVal(item, 'starty', starty - n * conf.boxMargin, Math.min);
×
164
        _self.updateVal(item, 'stopy', stopy + n * conf.boxMargin, Math.max);
×
165

×
166
        _self.updateVal(bounds.data, 'startx', startx - n * conf.boxMargin, Math.min);
×
167
        _self.updateVal(bounds.data, 'stopx', stopx + n * conf.boxMargin, Math.max);
×
168

×
169
        if (!(type === 'activation')) {
×
170
          _self.updateVal(item, 'startx', startx - n * conf.boxMargin, Math.min);
×
171
          _self.updateVal(item, 'stopx', stopx + n * conf.boxMargin, Math.max);
×
172

×
173
          _self.updateVal(bounds.data, 'starty', starty - n * conf.boxMargin, Math.min);
×
174
          _self.updateVal(bounds.data, 'stopy', stopy + n * conf.boxMargin, Math.max);
×
175
        }
×
176
      };
×
177
    }
×
178

×
179
    this.sequenceItems.forEach(updateFn());
×
180
  },
×
181
  insert: function (startx, starty, stopx, stopy) {
1✔
182
    const _startx = Math.min(startx, stopx);
×
183
    const _stopx = Math.max(startx, stopx);
×
184
    const _starty = Math.min(starty, stopy);
×
185
    const _stopy = Math.max(starty, stopy);
×
186

×
187
    this.updateVal(bounds.data, 'startx', _startx, Math.min);
×
188
    this.updateVal(bounds.data, 'starty', _starty, Math.min);
×
189
    this.updateVal(bounds.data, 'stopx', _stopx, Math.max);
×
190
    this.updateVal(bounds.data, 'stopy', _stopy, Math.max);
×
191

×
192
    this.updateBounds(_startx, _starty, _stopx, _stopy);
×
193
  },
×
194
  bumpVerticalPos: function (bump) {
1✔
195
    this.verticalPos = this.verticalPos + bump;
×
196
    this.data.stopy = this.verticalPos;
×
197
  },
×
198
  getVerticalPos: function () {
1✔
199
    return this.verticalPos;
×
200
  },
×
201
  getBounds: function () {
1✔
202
    return this.data;
×
203
  },
×
204
};
1✔
205

1✔
206
const fills = conf.sectionFills;
1✔
207
const textColours = conf.sectionColours;
1✔
208

1✔
209
export const drawTasks = function (diagram, tasks, verticalPos) {
1✔
210
  const conf = getConfig().journey;
×
211
  let lastSection = '';
×
212
  const sectionVHeight = conf.height * 2 + conf.diagramMarginY;
×
213
  const taskPos = verticalPos + sectionVHeight;
×
214

×
215
  let sectionNumber = 0;
×
216
  let fill = '#CCC';
×
217
  let colour = 'black';
×
218
  let num = 0;
×
219

×
220
  // Draw the tasks
×
221
  for (const [i, task] of tasks.entries()) {
×
222
    if (lastSection !== task.section) {
×
223
      fill = fills[sectionNumber % fills.length];
×
224
      num = sectionNumber % fills.length;
×
225
      colour = textColours[sectionNumber % textColours.length];
×
226

×
227
      // count how many consecutive tasks have the same section
×
228
      let taskInSectionCount = 0;
×
229
      const currentSection = task.section;
×
230
      for (let taskIndex = i; taskIndex < tasks.length; taskIndex++) {
×
231
        if (tasks[taskIndex].section == currentSection) {
×
232
          taskInSectionCount = taskInSectionCount + 1;
×
233
        } else {
×
234
          break;
×
235
        }
×
236
      }
×
237

×
238
      const section = {
×
239
        x: i * conf.taskMargin + i * conf.width + LEFT_MARGIN,
×
240
        y: 50,
×
241
        text: task.section,
×
242
        fill,
×
243
        num,
×
244
        colour,
×
245
        taskCount: taskInSectionCount,
×
246
      };
×
247

×
248
      svgDraw.drawSection(diagram, section, conf);
×
249
      lastSection = task.section;
×
250
      sectionNumber++;
×
251
    }
×
252

×
253
    // Collect the actors involved in the task
×
254
    const taskActors = task.people.reduce((acc, actorName) => {
×
255
      if (actors[actorName]) {
×
256
        acc[actorName] = actors[actorName];
×
257
      }
×
258

×
259
      return acc;
×
260
    }, {});
×
261

×
262
    // Add some rendering data to the object
×
263
    task.x = i * conf.taskMargin + i * conf.width + LEFT_MARGIN;
×
264
    task.y = taskPos;
×
265
    task.width = conf.diagramMarginX;
×
266
    task.height = conf.diagramMarginY;
×
267
    task.colour = colour;
×
268
    task.fill = fill;
×
269
    task.num = num;
×
270
    task.actors = taskActors;
×
271

×
272
    // Draw the box with the attached line
×
273
    svgDraw.drawTask(diagram, task, conf);
×
274
    bounds.insert(task.x, task.y, task.x + task.width + conf.taskMargin, 300 + 5 * 30); // stopY is the length of the descenders.
×
275
  }
×
276
};
×
277

1✔
278
export default {
1✔
279
  setConf,
1✔
280
  draw,
1✔
281
};
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc