Skip to content

Commit

Permalink
Merge pull request #7 from mermaid-js/develop
Browse files Browse the repository at this point in the history
update fork
  • Loading branch information
jgreywolf committed Dec 11, 2019
2 parents cf5d747 + d17a447 commit 9406bda
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 17 deletions.
2 changes: 2 additions & 0 deletions .github/release-drafter.yml
Expand Up @@ -10,6 +10,8 @@ categories:
- title: '🧰 Maintenance'
label: 'Type: Other'
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
sort-by: title
sort-direction: ascending
branches:
- develop
exclude-labels:
Expand Down
41 changes: 41 additions & 0 deletions cypress/integration/rendering/classDiagram.spec.js
Expand Up @@ -163,4 +163,45 @@ describe('Class diagram', () => {
);
cy.get('svg');
});

it('5: should render a simple class diagram with Generic class', () => {
imgSnapshotTest(
`
classDiagram
class Class01~T~
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
{}
);
cy.get('svg');
});

it('6: should render a simple class diagram with Generic class and relations', () => {
imgSnapshotTest(
`
classDiagram
Class01~T~ <|-- AveryLongClass : Cool
Class03~T~ *-- Class04~T~
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
{}
);
cy.get('svg');
});
});
37 changes: 37 additions & 0 deletions dist/index.html
Expand Up @@ -418,6 +418,43 @@
size()
}
</div>

<div class="mermaid">
classDiagram
class Class01~T~
Class01 : #size()
Class01 : -int chimp
Class01 : +int gorilla
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
size()
}
</div>

<div class="mermaid">
classDiagram
Class01~T~ <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03~T~ "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06
Class07~T~ .. Class08
Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : #size()
Class01 : -int chimp
Class01 : +int gorilla
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
size()
}
</div>

<div class="mermaid">
stateDiagram
State1
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -84,7 +84,8 @@
"puppeteer": "^1.17.0",
"sass-loader": "^7.1.0",
"start-server-and-test": "^1.10.6",
"webpack": "^4.27.1",
"terser-webpack-plugin": "^2.2.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.4.1",
"webpack-node-externals": "^1.7.2",
Expand Down
32 changes: 27 additions & 5 deletions src/diagrams/class/classDb.js
Expand Up @@ -3,17 +3,32 @@ import { logger } from '../../logger';
let relations = [];
let classes = {};

const splitClassNameAndType = function(id) {
let genericType = '';
let className = id;

if (id.indexOf('~') > 0) {
let split = id.split('~');
className = split[0];
genericType = split[1];
}

return { className: className, type: genericType };
};

/**
* Function called by parser when a node definition has been found.
* @param id
* @public
*/
export const addClass = function(id) {
let classId = splitClassNameAndType(id);
// Only add class if not exists
if (typeof classes[id] !== 'undefined') return;
if (typeof classes[classId.className] !== 'undefined') return;

classes[id] = {
id: id,
classes[classId.className] = {
id: classId.className,
type: classId.type,
methods: [],
members: [],
annotations: []
Expand All @@ -40,6 +55,10 @@ export const addRelation = function(relation) {
logger.debug('Adding relation: ' + JSON.stringify(relation));
addClass(relation.id1);
addClass(relation.id2);

relation.id1 = splitClassNameAndType(relation.id1).className;
relation.id2 = splitClassNameAndType(relation.id2).className;

relations.push(relation);
};

Expand All @@ -51,7 +70,8 @@ export const addRelation = function(relation) {
* @public
*/
export const addAnnotation = function(className, annotation) {
classes[className].annotations.push(annotation);
const validatedClassName = splitClassNameAndType(className).className;
classes[validatedClassName].annotations.push(annotation);
};

/**
Expand All @@ -64,7 +84,9 @@ export const addAnnotation = function(className, annotation) {
* @public
*/
export const addMember = function(className, member) {
const theClass = classes[className];
const validatedClassName = splitClassNameAndType(className).className;
const theClass = classes[validatedClassName];

if (typeof member === 'string') {
// Member can contain white spaces, we trim them out
const memberString = member.trim();
Expand Down
42 changes: 42 additions & 0 deletions src/diagrams/class/classDiagram.spec.js
Expand Up @@ -56,6 +56,33 @@ describe('class diagram, ', function () {
parser.parse(str);
});

it('should handle generic class', function() {
const str =
'classDiagram\n' +
'class Car~T~\n' +
'Driver -- Car : drives >\n' +
'Car *-- Wheel : have 4 >\n' +
'Car -- Person : < owns';

parser.parse(str);
});

it('should handle generic class with brackets', function() {
const str =
'classDiagram\n' +
'class Dummy_Class~T~ {\n' +
'String data\n' +
' void methods()\n' +
'}\n' +
'\n' +
'class Flight {\n' +
' flightNumber : Integer\n' +
' departureTime : Date\n' +
'}';

parser.parse(str);
});

it('should handle class definitions', function() {
const str =
'classDiagram\n' +
Expand Down Expand Up @@ -326,6 +353,21 @@ describe('class diagram, ', function () {
expect(relations[3].relation.lineType).toBe(classDb.lineType.DOTTED_LINE);
});

it('should handle generic class with relation definitions', function () {
const str = 'classDiagram\n' + 'Class01~T~ <|-- Class02';

parser.parse(str);

const relations = parser.yy.getRelations();

expect(parser.yy.getClass('Class01').id).toBe('Class01');
expect(parser.yy.getClass('Class01').type).toBe('T');
expect(parser.yy.getClass('Class02').id).toBe('Class02');
expect(relations[0].relation.type1).toBe(classDb.relationType.EXTENSION);
expect(relations[0].relation.type2).toBe('none');
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE);
});

it('should handle class annotations', function () {
const str = 'classDiagram\n' + 'class Class1\n' + '<<interface>> Class1';
parser.parse(str);
Expand Down
8 changes: 7 additions & 1 deletion src/diagrams/class/classRenderer.js
Expand Up @@ -318,10 +318,16 @@ const drawClass = function(elem, classDef) {
isFirst = false;
});

let classTitleString = classDef.id;

if (classDef.type !== undefined && classDef.type !== '') {
classTitleString += '<' + classDef.type + '>';
}

// add class title
const classTitle = title
.append('tspan')
.text(classDef.id)
.text(classTitleString)
.attr('class', 'title');

// If class has annotations the title needs to have an offset of the text height
Expand Down
9 changes: 7 additions & 2 deletions src/diagrams/class/parser/classDiagram.jison
Expand Up @@ -6,7 +6,7 @@

/* lexical grammar */
%lex
%x string struct
%x string generic struct

%%
\%\%[^\n]*\n* /* do nothing */
Expand All @@ -23,6 +23,9 @@
"class" return 'CLASS';
"<<" return 'ANNOTATION_START';
">>" return 'ANNOTATION_END';
[~] this.begin("generic");
<generic>[~] this.popState();
<generic>[^~]* return "GENERICTYPE";
["] this.begin("string");
<string>["] this.popState();
<string>[^"]* return "STR";
Expand All @@ -36,7 +39,7 @@
\s*o return 'AGGREGATION';
\-\- return 'LINE';
\.\. return 'DOTTED_LINE';
":"[^\n;]+ return 'LABEL';
":"[^\n;]+ return 'LABEL';
\- return 'MINUS';
"." return 'DOT';
\+ return 'PLUS';
Expand Down Expand Up @@ -136,6 +139,8 @@ statements
className
: alphaNumToken className { $$=$1+$2; }
| alphaNumToken { $$=$1; }
| alphaNumToken GENERICTYPE className { $$=$1+'~'+$2+$3; }
| alphaNumToken GENERICTYPE { $$=$1+'~'+$2; }
;

statement
Expand Down

0 comments on commit 9406bda

Please sign in to comment.