diff --git a/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison b/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison index d15204f9a..455b27df5 100644 --- a/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison +++ b/src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison @@ -103,8 +103,16 @@ statement ; note_statement - : 'note' placement actor text2 {$$=[$3,{type:'addNote', placement:$2, actor:$3.actor, text:$4}];} - | 'note' 'over' spaceList actor_pair actor + : 'note' placement actor text2 + { + $$ = [$3, {type:'addNote', placement:$2, actor:$3.actor, text:$4}];} + | 'note' 'over' actor_pair text2 + { + // Coerce actor_pair into a [to, from, ...] array + $2 = [].concat($3, $3).slice(0, 2); + $2[0] = $2[0].actor; + $2[1] = $2[1].actor; + $$ = [$3, {type:'addNote', placement:yy.PLACEMENT.OVER, actor:$2.slice(0, 2), text:$4}];} ; spaceList @@ -112,8 +120,8 @@ spaceList | SPACE ; actor_pair - : actor { $$ = $1; } - | actor ',' actor { $$ = [$1, $3]; } + : actor ',' actor { $$ = [$1, $3]; } + | actor { $$ = $1; } ; placement diff --git a/src/diagrams/sequenceDiagram/sequenceDb.js b/src/diagrams/sequenceDiagram/sequenceDb.js index 5b302cd12..1404ef605 100644 --- a/src/diagrams/sequenceDiagram/sequenceDb.js +++ b/src/diagrams/sequenceDiagram/sequenceDb.js @@ -77,8 +77,11 @@ exports.PLACEMENT = { exports.addNote = function (actor, placement, message){ var note = {actor:actor, placement: placement, message:message}; + // Coerce actor into a [to, from, ...] array + var actors = [].concat(actor, actor); + notes.push(note); - messages.push({from:actor, to:actor, message:message, type:exports.LINETYPE.NOTE, placement: placement}); + messages.push({from:actors[0], to:actors[1], message:message, type:exports.LINETYPE.NOTE, placement: placement}); }; @@ -132,4 +135,4 @@ exports.apply = function(param){ break; } } -}; \ No newline at end of file +}; diff --git a/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js b/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js index d154e1175..671acb0f9 100644 --- a/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js +++ b/src/diagrams/sequenceDiagram/sequenceDiagram.spec.js @@ -231,6 +231,31 @@ describe('when parsing a sequenceDiagram',function() { expect(messages[0].from).toBe('Alice'); expect(messages[2].from).toBe('John'); }); + it('it should handle notes over a single actor', function () { + var str = 'sequenceDiagram\n' + + 'Alice->Bob: Hello Bob, how are you?\n' + + 'Note over Bob: Bob thinks\n'; + + sq.parse(str); + + var messages = sq.yy.getMessages(); + expect(messages[1].from).toBe('Bob'); + expect(messages[1].to).toBe('Bob'); + }); + it('it should handle notes over multiple actors', function () { + var str = 'sequenceDiagram\n' + + 'Alice->Bob: Hello Bob, how are you?\n' + + 'Note over Alice,Bob: confusion\n' + + 'Note over Bob,Alice: resolution\n'; + + sq.parse(str); + + var messages = sq.yy.getMessages(); + expect(messages[1].from).toBe('Alice'); + expect(messages[1].to).toBe('Bob'); + expect(messages[2].from).toBe('Bob'); + expect(messages[2].to).toBe('Alice'); + }); it('it should handle loop statements a sequenceDiagram', function () { var str = 'sequenceDiagram\n' + 'Alice->Bob: Hello Bob, how are you?\n\n' + @@ -623,7 +648,23 @@ describe('when rendering a sequenceDiagram',function() { expect(bounds.stopy ).toBe(conf.height); }); - it('it should handle one actor and a note', function () { + it('it should handle one actor and a centered note', function () { + sd.bounds.init(); + var str = 'sequenceDiagram\n' + + 'participant Alice\n' + + 'Note over Alice: Alice thinks\n'; + + sq.parse(str); + sd.draw(str,'tst'); + + var bounds = sd.bounds.getBounds(); + expect(bounds.startx).toBe(0); + expect(bounds.starty).toBe(0); + expect(bounds.stopx ).toBe( conf.width); + // 10 comes from mock of text height + expect(bounds.stopy ).toBe( conf.height + conf.boxMargin + 2*conf.noteMargin +10); + }); + it('it should handle one actor and a note to the left', function () { sd.bounds.init(); var str = 'sequenceDiagram\n' + 'participant Alice\n' + @@ -668,7 +709,22 @@ describe('when rendering a sequenceDiagram',function() { expect(bounds.starty).toBe(0); expect(bounds.stopx ).toBe(conf.width*2 + conf.actorMargin); expect(bounds.stopy ).toBe(0 + conf.messageMargin + conf.height); + }); + it('it should handle two actors and two centered shared notes', function () { + sd.bounds.init(); + var str = 'sequenceDiagram\n' + + 'Alice->Bob: Hello Bob, how are you?\n'+ + 'Note over Alice,Bob: Looks\n' + + 'Note over Bob,Alice: Looks back\n'; + sq.parse(str); + sd.draw(str,'tst'); + + var bounds = sd.bounds.getBounds(); + expect(bounds.startx).toBe(0); + expect(bounds.starty).toBe(0); + expect(bounds.stopx ).toBe(conf.width*2 + conf.actorMargin); + expect(bounds.stopy ).toBe( conf.height + conf.messageMargin + 2*(conf.boxMargin + 2*conf.noteMargin + 10)); }); it('it should draw two actors and two messages', function () { sd.bounds.init(); diff --git a/src/diagrams/sequenceDiagram/sequenceRenderer.js b/src/diagrams/sequenceDiagram/sequenceRenderer.js index 7b2f6eb87..59d5fa417 100644 --- a/src/diagrams/sequenceDiagram/sequenceRenderer.js +++ b/src/diagrams/sequenceDiagram/sequenceRenderer.js @@ -129,11 +129,11 @@ exports.bounds = { * @param pos The position if the actor in the liost of actors * @param description The text in the box */ -var drawNote = function(elem, startx, verticalPos, msg){ +var drawNote = function(elem, startx, verticalPos, msg, forceWidth){ var rect = svgDraw.getNoteRect(); rect.x = startx; rect.y = verticalPos; - rect.width = conf.width; + rect.width = forceWidth || conf.width; rect.class = 'note'; var g = elem.append('g'); @@ -147,21 +147,19 @@ var drawNote = function(elem, startx, verticalPos, msg){ textObj.text = msg.message; textObj.class = 'noteText'; - var textElem = svgDraw.drawText(g,textObj, conf.width-conf.noteMargin); + var textElem = svgDraw.drawText(g,textObj, rect.width-conf.noteMargin); var textHeight = textElem[0][0].getBBox().height; - if(textHeight > conf.width){ + if(!forceWidth && textHeight > conf.width){ textElem.remove(); g = elem.append('g'); - //textObj.x = textObj.x - conf.width; - //textElem = svgDraw.drawText(g,textObj, 2*conf.noteMargin); - textElem = svgDraw.drawText(g,textObj, 2*conf.width-conf.noteMargin); + textElem = svgDraw.drawText(g,textObj, 2*rect.width-conf.noteMargin); textHeight = textElem[0][0].getBBox().height; - rectElem.attr('width',2*conf.width); - exports.bounds.insert(startx, verticalPos, startx + 2*conf.width, verticalPos + 2*conf.noteMargin + textHeight); + rectElem.attr('width',2*rect.width); + exports.bounds.insert(startx, verticalPos, startx + 2*rect.width, verticalPos + 2*conf.noteMargin + textHeight); }else{ - exports.bounds.insert(startx, verticalPos, startx + conf.width, verticalPos + 2*conf.noteMargin + textHeight); + exports.bounds.insert(startx, verticalPos, startx + rect.width, verticalPos + 2*conf.noteMargin + textHeight); } rectElem.attr('height',textHeight+ 2*conf.noteMargin); @@ -290,6 +288,7 @@ module.exports.draw = function (text, id) { var startx; var stopx; + var forceWidth; // Fetch data from the parsing var actors = sq.yy.getActors(); @@ -312,13 +311,19 @@ module.exports.draw = function (text, id) { startx = actors[msg.from].x; stopx = actors[msg.to].x; - if(msg.placement !== 0){ - // Right of + if(msg.placement === sq.yy.PLACEMENT.RIGHTOF){ drawNote(diagram, startx + (conf.width + conf.actorMargin)/2, exports.bounds.getVerticalPos(), msg); - }else{ - // Left of + }else if(msg.placement === sq.yy.PLACEMENT.LEFTOF){ drawNote(diagram, startx - (conf.width + conf.actorMargin)/2, exports.bounds.getVerticalPos(), msg); + }else if(msg.to === msg.from) { + // Single-actor over + drawNote(diagram, startx, exports.bounds.getVerticalPos(), msg); + }else{ + // Multi-actor over + forceWidth = Math.abs(startx - stopx) + conf.actorMargin; + drawNote(diagram, (startx + stopx + conf.width - forceWidth)/2, exports.bounds.getVerticalPos(), msg, + forceWidth); } break; case sq.yy.LINETYPE.LOOP_START: