Added support for a new link syntax per recommendation:

link <actor>: <label> @ <link-url>

Removed documentation for class/icon definitions until we can finalize the design for this.

Fixed prettier errors.
This commit is contained in:
eajenkins 2021-09-10 16:27:07 -07:00
parent b3210ed2ef
commit 85d33ecccd
10 changed files with 121 additions and 90 deletions

View File

@ -564,6 +564,7 @@ context('Sequence diagram', () => {
links a: {"Repo": "https://www.contoso.com/repo", "Swagger": "https://www.contoso.com/swagger"}
links j: {"Repo": "https://www.contoso.com/repo"}
links a: {"Dashboard": "https://www.contoso.com/dashboard", "On-Call": "https://www.contoso.com/oncall"}
link a: Contacts @ https://contacts.contoso.com/?contact=alice@contoso.com
a->>j: Hello John, how are you?
j-->>a: Great!
`,

View File

@ -409,6 +409,27 @@ sequenceDiagram
## Actor Menus
Actors can have popup-menus containing individualized links to external pages. For example, if an actor represented a web service, useful links might include a link to the service health dashboard, repo containing the code for the service, or a wiki page describing the service.
This can be configured by adding one or more link lines with the format:
link <actor>: <link-label> @ <link-url>
```
sequenceDiagram
participant Alice
participant John
link Alice: Dashboard @ https://dashboard.contoso.com/alice
link Alice: Wiki @ https://wiki.contoso.com/alice
link John: Dashboard @ https://dashboard.contoso.com/john
link John: Wiki @ https://wiki.contoso.com/john
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
```
#### Advanced Menu Syntax
There is an advanced syntax that relies on JSON formatting. If you are comfortable with JSON format, then this exists as well.
This can be configured by adding the links lines with the format:
links <actor>: <json-formatted link-name link-url pairs>
@ -426,38 +447,6 @@ sequenceDiagram
Alice-)John: See you later!
```
## Actor Individualized Styles & Icons
Actors can have individualized styling including an embedded icon.
This can be configured by adding the properties lines with this format:
properties <actor>: { "class": "<css className>", "icon": @<built-in-icon-name> -or- <url to an image file>
>
```
sequenceDiagram
participant Alice
participant John
properties Alice: {"class": "scheduled-job-actor", "icon": "@clock"}
properties John: {"class": "database-service-actor", "icon": "https://icons.contoso.com/database.svg"}
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
```
```mermaid
sequenceDiagram
participant Alice
participant John
properties Alice: {"icon": "@clock"}
properties John: {"icon": "@database"}
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
```
Built-in icon names include @clock, @database, @computer.
## Styling
Styling of a sequence diagram is done by defining a number of css classes. During rendering these classes are extracted from the file located at src/themes/sequence.scss

View File

@ -372,7 +372,7 @@ const config = {
*| forceMenus | forces actor popup menus to always be visible (to support E2E testing). | Boolean| Required | True, False |
*
* **Notes:**
*
*
* Default value: false.
*/
forceMenus: false,

View File

@ -48,6 +48,7 @@
"left of" return 'left_of';
"right of" return 'right_of';
"links" return 'links';
"link" return 'link';
"properties" return 'properties';
"details" return 'details';
"over" return 'over';
@ -56,7 +57,7 @@
"deactivate" { this.begin('ID'); return 'deactivate'; }
"title" return 'title';
"sequenceDiagram" return 'SD';
"autonumber" return 'autonumber';
"autonumber" return 'autonumber';
"," return ',';
";" return 'NEWLINE';
[^\+\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)))[\-]*[^\+\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; }
@ -66,8 +67,8 @@
"-->" return 'DOTTED_OPEN_ARROW';
\-[x] return 'SOLID_CROSS';
\-\-[x] return 'DOTTED_CROSS';
\-[\)] return 'SOLID_POINT';
\-\-[\)] return 'DOTTED_POINT';
\-[\)] return 'SOLID_POINT';
\-\-[\)] return 'DOTTED_POINT';
":"(?:(?:no)?wrap:)?[^#\n;]+ return 'TXT';
"+" return '+';
"-" return '-';
@ -114,6 +115,7 @@ statement
| 'deactivate' actor 'NEWLINE' {$$={type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $2};}
| note_statement 'NEWLINE'
| links_statement 'NEWLINE'
| link_statement 'NEWLINE'
| properties_statement 'NEWLINE'
| details_statement 'NEWLINE'
| title text2 'NEWLINE' {$$=[{type:'setTitle', text:$2}]}
@ -183,6 +185,13 @@ links_statement
}
;
link_statement
: 'link' actor text2
{
$$ = [$2, {type:'addALink', actor:$2.actor, text:$3}];
}
;
properties_statement
: 'properties' actor text2
{

View File

@ -218,8 +218,24 @@ export const addLinks = function (actorId, text) {
const links = JSON.parse(text.text);
// add the deserialized text to the actor's links field.
insertLinks(actor, links);
} catch (e) {
log.error('error while parsing actor link text', e);
}
catch (e) {
};
export const addALink = function (actorId, text) {
// find the actor
const actor = getActor(actorId);
try {
const links = {};
var sep = text.text.indexOf('@');
var label = text.text.slice(0, sep - 1).trim();
var link = text.text.slice(sep + 1).trim();
links[label] = link;
// add the deserialized text to the actor's links field.
insertLinks(actor, links);
} catch (e) {
log.error('error while parsing actor link text', e);
}
};
@ -227,8 +243,7 @@ export const addLinks = function (actorId, text) {
function insertLinks(actor, links) {
if (actor.links == null) {
actor.links = links;
}
else {
} else {
for (let key in links) {
actor.links[key] = links[key];
}
@ -243,8 +258,7 @@ export const addProperties = function (actorId, text) {
const properties = JSON.parse(text.text);
// add the deserialized text to the actor's property field.
insertProperties(actor, properties);
}
catch (e) {
} catch (e) {
log.error('error while parsing actor properties text', e);
}
};
@ -252,8 +266,7 @@ export const addProperties = function (actorId, text) {
function insertProperties(actor, properties) {
if (actor.properties == null) {
actor.properties = properties;
}
else {
} else {
for (let key in properties) {
actor.properties[key] = properties[key];
}
@ -270,15 +283,14 @@ export const addDetails = function (actorId, text) {
const text = elem.innerHTML;
const details = JSON.parse(text);
// add the deserialized text to the actor's property field.
if (details["properties"]) {
insertProperties(actor, details["properties"]);
if (details['properties']) {
insertProperties(actor, details['properties']);
}
if (details["links"]) {
insertLinks(actor, details["links"]);
if (details['links']) {
insertLinks(actor, details['links']);
}
}
catch (e) {
} catch (e) {
log.error('error while parsing actor details text', e);
}
};
@ -318,6 +330,9 @@ export const apply = function (param) {
case 'addLinks':
addLinks(param.actor, param.text);
break;
case 'addALink':
addALink(param.actor, param.text);
break;
case 'addProperties':
addProperties(param.actor, param.text);
break;

View File

@ -908,6 +908,9 @@ participant c as Charlie
links a: { "Repo": "https://repo.contoso.com/", "Dashboard": "https://dashboard.contoso.com/" }
links b: { "Dashboard": "https://dashboard.contoso.com/" }
links a: { "On-Call": "https://oncall.contoso.com/?svc=alice" }
link a: Endpoint @ https://alice.contoso.com
link a: Swagger @ https://swagger.contoso.com
link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com
`;
console.log(str);
@ -919,6 +922,9 @@ links a: { "On-Call": "https://oncall.contoso.com/?svc=alice" }
expect(actors.b.links["Dashboard"]).toBe("https://dashboard.contoso.com/");
expect(actors.a.links["On-Call"]).toBe("https://oncall.contoso.com/?svc=alice");
expect(actors.c.links["Dashboard"]).toBe(undefined);
expect(actors.a.links["Endpoint"]).toBe("https://alice.contoso.com");
expect(actors.a.links["Swagger"]).toBe("https://swagger.contoso.com");
expect(actors.a.links["Tests"]).toBe("https://tests.contoso.com/?svc=alice@contoso.com");
});
it('it should handle properties', function () {

View File

@ -446,7 +446,7 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
bounds.bumpVerticalPos(conf.height);
};
export const drawActorsPopup = function(diagram, actors, actorKeys) {
export const drawActorsPopup = function (diagram, actors, actorKeys) {
var maxHeight = 0;
var maxWidth = 0;
for (let i = 0; i < actorKeys.length; i++) {
@ -870,7 +870,7 @@ const getMaxMessageWidthPerActor = function (actors, messages) {
return maxMessageWidthPerActor;
};
const getRequiredPopupWidth = function(actor) {
const getRequiredPopupWidth = function (actor) {
let requiredPopupWidth = 0;
const textFont = actorFont(conf);
for (let key in actor.links) {
@ -881,8 +881,8 @@ const getRequiredPopupWidth = function(actor) {
}
}
return requiredPopupWidth;
}
return requiredPopupWidth;
};
/**
* This will calculate the optimal margin for each given actor, for a given

View File

@ -95,6 +95,17 @@ const getStyles = (options) =>
fill: ${options.activationBkgColor};
stroke: ${options.activationBorderColor};
}
.actorPopupMenu {
position: absolute;
}
.actorPopupMenuPanel {
position: absolute;
fill: ${options.actorBkg};
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));
}
`;
export default getStyles;

File diff suppressed because one or more lines are too long

View File

@ -94,14 +94,3 @@ text.actor > tspan {
fill: $activationBkgColor;
stroke: $activationBorderColor;
}
.actorPopupMenu {
position: absolute;
}
.actorPopupMenuPanel {
position: absolute;
fill: $actorBkg;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));
}