mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-28 09:24:10 +01:00
Compare commits
10 Commits
feature/66
...
bugfix/545
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6629b20d37 | ||
|
|
fed8a523a4 | ||
|
|
33b4946e21 | ||
|
|
76e17ffd20 | ||
|
|
60f633101c | ||
|
|
7def6eecbf | ||
|
|
6cf17a9852 | ||
|
|
07bd381197 | ||
|
|
3bb9416537 | ||
|
|
3e6b7bc3df |
5
.changeset/chilly-words-march.md
Normal file
5
.changeset/chilly-words-march.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Correct viewBox casing and make SVGs responsive
|
||||
@@ -99,6 +99,7 @@ export const openURLAndVerifyRendering = (
|
||||
cy.visit(url);
|
||||
cy.window().should('have.property', 'rendered', true);
|
||||
cy.get('svg').should('be.visible');
|
||||
cy.get('svg').should('not.have.attr', 'viewbox');
|
||||
|
||||
if (validation) {
|
||||
cy.get('svg').should(validation);
|
||||
|
||||
@@ -33,15 +33,12 @@
|
||||
Animal: +mate()
|
||||
|
||||
class Duck{
|
||||
<<injected>>
|
||||
<<interface>>
|
||||
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-List~int~ sizeInFeet
|
||||
-Listint sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
@@ -59,8 +56,6 @@
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
|
||||
<<interface>> Class01
|
||||
<<injected>> Class01
|
||||
|
||||
Class03 "0" *-- "0..n" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 .. Class08
|
||||
@@ -161,7 +156,7 @@
|
||||
~InternalProperty : string
|
||||
~AnotherInternalProperty : List~List~string~~
|
||||
}
|
||||
class People List~Person~
|
||||
class People List~List~Person~~
|
||||
</pre>
|
||||
<hr />
|
||||
<pre class="mermaid">
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Multiple Annotations Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
color: #333;
|
||||
}
|
||||
.mermaid {
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.description {
|
||||
background: #e8f4fd;
|
||||
border-left: 4px solid #0066cc;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Multiple Annotations Test - Issue #6680 Fix</h1>
|
||||
|
||||
<div class="description">
|
||||
<strong>Testing:</strong> Multiple stereotypes/annotations should now render correctly using
|
||||
external and inline annotation methods.
|
||||
</div>
|
||||
|
||||
<h2>Baseline - no annotations</h2>
|
||||
<pre class="mermaid">
|
||||
classDiagram
|
||||
class Shape
|
||||
class Circle
|
||||
class Triangle
|
||||
</pre>
|
||||
|
||||
<h2>#0 Baseline - single annotation</h2>
|
||||
<pre class="mermaid">
|
||||
classDiagram
|
||||
class Shape
|
||||
class Circle
|
||||
class Triangle
|
||||
|
||||
<<injected>> Shape
|
||||
</pre>
|
||||
|
||||
<h2>Method 1: External/Next-line Annotations</h2>
|
||||
<div class="description">
|
||||
External annotations defined on the line after the class definition:
|
||||
</div>
|
||||
<pre class="mermaid">
|
||||
classDiagram
|
||||
class Shape
|
||||
<<interface>> <<injected>> Shape
|
||||
class Circle
|
||||
<<abstract>> <<serializable>> Circle
|
||||
class Triangle
|
||||
<<interface>> <<cached>> <<singleton>> Triangle
|
||||
</pre>
|
||||
|
||||
<h2>Method 2: Inline Annotations</h2>
|
||||
<div class="description">Inline annotations defined directly with the class definition:</div>
|
||||
<pre class="mermaid">
|
||||
classDiagram
|
||||
class Shape <<interface>> <<injected>>
|
||||
class Circle <<abstract>> <<serializable>>
|
||||
class Square <<service>> <<singleton>> <<cached>>
|
||||
class Triangle <<interface>> <<component>> <<transient>>
|
||||
</pre>
|
||||
|
||||
<h2>Method 3: Mixed Methods Test</h2>
|
||||
<div class="description">Combination of both external and inline annotation methods:</div>
|
||||
<pre class="mermaid">
|
||||
classDiagram
|
||||
class Component <<interface>> <<injected>>
|
||||
class Service
|
||||
<<abstract>> <<singleton>> Service
|
||||
class Repository <<dao>> <<cached>>
|
||||
class Controller
|
||||
<<rest>> <<secured>> Controller
|
||||
</pre>
|
||||
|
||||
<h2>Real-world Example</h2>
|
||||
<div class="description">A practical example with relationships and multiple annotations:</div>
|
||||
<pre class="mermaid">
|
||||
classDiagram
|
||||
class BaseService <<abstract>> <<injectable>>
|
||||
class UserService <<service>> <<singleton>>
|
||||
class UserRepository <<repository>> <<cached>>
|
||||
class UserController
|
||||
<<controller>> <<secured>> UserController
|
||||
|
||||
BaseService <|-- UserService
|
||||
UserService --> UserRepository
|
||||
UserController --> UserService
|
||||
</pre>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'default',
|
||||
logLevel: 'debug',
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -79,7 +79,7 @@ classDiagram
|
||||
UML provides mechanisms to represent class members, such as attributes and methods, and additional information about them.
|
||||
A single instance of a class in the diagram contains three compartments:
|
||||
|
||||
- The top compartment contains the name of the class. It is printed in bold and centered, and the first letter is capitalized. It may also contain optional annotation/stereotype text describing the nature of the class.
|
||||
- The top compartment contains the name of the class. It is printed in bold and centered, and the first letter is capitalized. It may also contain optional annotation text describing the nature of the class.
|
||||
- The middle compartment contains the attributes of the class. They are left-aligned and the first letter is lowercase.
|
||||
- The bottom compartment contains the operations the class can execute. They are also left-aligned and the first letter is lowercase.
|
||||
|
||||
@@ -287,6 +287,7 @@ To describe the visibility (or encapsulation) of an attribute or method/function
|
||||
>
|
||||
> - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*`
|
||||
> - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$`
|
||||
> - `$*` OR `*$` Both e.g: `someAbstractStaticMethod()$*` or `someAbstractStaticMethod() int$*`
|
||||
|
||||
> _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end:
|
||||
>
|
||||
@@ -534,7 +535,7 @@ classDiagram
|
||||
Galaxy --> "many" Star : Contains
|
||||
```
|
||||
|
||||
## Annotations on classes (stereotypes)
|
||||
## Annotations on classes
|
||||
|
||||
It is possible to annotate classes with markers to provide additional metadata about the class. This can give a clearer indication about its nature. Some common annotations include:
|
||||
|
||||
@@ -543,33 +544,57 @@ It is possible to annotate classes with markers to provide additional metadata a
|
||||
- `<<Service>>` To represent a service class
|
||||
- `<<Enumeration>>` To represent an enum
|
||||
|
||||
Annotations are defined within the opening `<<` and closing `>>`. There are multiple ways to add an annotation to a class, and any of them will output the same result:
|
||||
Annotations are defined within the opening `<<` and closing `>>`. There are two ways to add an annotation to a class, and either way the output will be same:
|
||||
|
||||
- **Inline with the class definition** (Recommended for consistency):
|
||||
> **Tip:**\
|
||||
> In Mermaid class diagrams, annotations like `<<interface>>` can be attached in two ways:
|
||||
>
|
||||
> - **Inline with the class definition** (Recommended for consistency):
|
||||
>
|
||||
> ```mermaid-example
|
||||
> classDiagram
|
||||
> class Shape <<interface>>
|
||||
> ```
|
||||
>
|
||||
> ```mermaid
|
||||
> classDiagram
|
||||
> class Shape <<interface>>
|
||||
> ```
|
||||
>
|
||||
> - **Separate line after the class definition**:
|
||||
>
|
||||
> ```mermaid-example
|
||||
> classDiagram
|
||||
> class Shape
|
||||
> <<interface>> Shape
|
||||
> ```
|
||||
>
|
||||
> ```mermaid
|
||||
> classDiagram
|
||||
> class Shape
|
||||
> <<interface>> Shape
|
||||
> ```
|
||||
>
|
||||
> Both methods are fully supported and produce identical diagrams.\
|
||||
> However, it is recommended to use the **inline style** for better readability and consistent formatting across diagrams.
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape <<interface>>
|
||||
```
|
||||
- In a **_separate line_** after a class is defined:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Shape <<interface>>
|
||||
```
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape
|
||||
<<interface>> Shape
|
||||
Shape : noOfVertices
|
||||
Shape : draw()
|
||||
```
|
||||
|
||||
- **Separate line after the class definition**:
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape
|
||||
<<interface>> Shape
|
||||
```
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Shape
|
||||
<<interface>> Shape
|
||||
```
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Shape
|
||||
<<interface>> Shape
|
||||
Shape : noOfVertices
|
||||
Shape : draw()
|
||||
```
|
||||
|
||||
- In a **_nested structure_** along with the class definition:
|
||||
|
||||
@@ -609,30 +634,6 @@ class Color{
|
||||
|
||||
```
|
||||
|
||||
For the case of multiple annotations the syntax is similar, you can add other annotations on the same line, or in a nested class definition add an annotation on a new line, as follows:
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape{
|
||||
<<interface>>
|
||||
<<injected>>
|
||||
noOfVertices
|
||||
draw()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Shape{
|
||||
<<interface>>
|
||||
<<injected>>
|
||||
noOfVertices
|
||||
draw()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
||||
Comments can be entered within a class diagram, which will be ignored by the parser. Comments need to be on their own line, and must be prefaced with `%%` (double percent signs). Any text until the next newline will be treated as a comment, including any class diagram syntax.
|
||||
|
||||
@@ -237,7 +237,6 @@ export class ClassDB implements DiagramDB {
|
||||
* @public
|
||||
*/
|
||||
public addAnnotation(className: string, annotation: string) {
|
||||
this.addClass(className);
|
||||
const validatedClassName = this.splitClassNameAndType(className).className;
|
||||
this.classes.get(validatedClassName)!.annotations.push(annotation);
|
||||
}
|
||||
@@ -266,6 +265,10 @@ export class ClassDB implements DiagramDB {
|
||||
theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2)));
|
||||
} else if (memberString.indexOf(')') > 0) {
|
||||
//its a method
|
||||
if (memberString.length < 2) {
|
||||
// Too short to be a method, ignore
|
||||
return;
|
||||
}
|
||||
theClass.methods.push(new ClassMember(memberString, 'method'));
|
||||
} else if (memberString) {
|
||||
theClass.members.push(new ClassMember(memberString, 'attribute'));
|
||||
|
||||
@@ -241,31 +241,6 @@ describe('given a basic class diagram, ', function () {
|
||||
expect(c1.annotations[0]).toBe('interface');
|
||||
});
|
||||
|
||||
it('should handle multiple external annotations', () => {
|
||||
const str = `classDiagram
|
||||
<<interface>><<service>> Car
|
||||
Car : int size
|
||||
Car : int age`;
|
||||
|
||||
parser.parse(str);
|
||||
const carClass = classDb.getClass('Car');
|
||||
expect(carClass.annotations).toHaveLength(2);
|
||||
expect(carClass.annotations).toContain('interface');
|
||||
expect(carClass.annotations).toContain('service');
|
||||
});
|
||||
|
||||
it('should handle multiple annotations inline with class definition', () => {
|
||||
const str = `classDiagram
|
||||
class Car <<interface>> <<service>>
|
||||
Car : int size`;
|
||||
|
||||
parser.parse(str);
|
||||
const carClass = classDb.getClass('Car');
|
||||
expect(carClass.annotations).toHaveLength(2);
|
||||
expect(carClass.annotations).toContain('interface');
|
||||
expect(carClass.annotations).toContain('service');
|
||||
});
|
||||
|
||||
it('should parse a class with text label and css class shorthand', () => {
|
||||
const str = 'classDiagram\n' + 'class C1["Class 1 with text label"]:::styleClass';
|
||||
|
||||
@@ -947,28 +922,6 @@ foo()
|
||||
expect(actual.methods.length).toBe(1);
|
||||
expect(actual.annotations[0]).toBe('interface');
|
||||
});
|
||||
|
||||
it('should handle multiple consecutive annotations on a single class', function () {
|
||||
const str = 'classDiagram\n' + '<<interface>><<injected>> Class1';
|
||||
|
||||
parser.parse(str);
|
||||
|
||||
const actual = parser.yy.getClass('Class1');
|
||||
expect(actual.annotations.length).toBe(2);
|
||||
expect(actual.annotations).toContain('interface');
|
||||
expect(actual.annotations).toContain('injected');
|
||||
});
|
||||
|
||||
it('should handle multiple separate annotations on a single class', function () {
|
||||
const str = 'classDiagram\n' + '<<interface>> Class1\n' + '<<injected>> Class1';
|
||||
|
||||
parser.parse(str);
|
||||
|
||||
const actual = parser.yy.getClass('Class1');
|
||||
expect(actual.annotations.length).toBe(2);
|
||||
expect(actual.annotations).toContain('interface');
|
||||
expect(actual.annotations).toContain('injected');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ClassMember } from './classTypes.js';
|
||||
|
||||
describe('ClassTypes - Enhanced Abstract and Static Combinations', () => {
|
||||
// Test constants to match original test structure
|
||||
const staticCssStyle = 'text-decoration:underline;';
|
||||
const abstractCssStyle = 'font-style:italic;';
|
||||
const abstractStaticCssStyle = 'text-decoration:underline;font-style:italic;';
|
||||
|
||||
describe('Enhanced parseClassifier functionality', () => {
|
||||
describe('when the attribute has static "$" modifier', () => {
|
||||
it('should parse the display text correctly and apply static css style', () => {
|
||||
const str = 'name String$';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('name String');
|
||||
expect(displayDetails.cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has abstract "*" modifier', () => {
|
||||
it('should parse the display text correctly and apply abstract css style', () => {
|
||||
const str = 'name String*';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('name String');
|
||||
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has abstract static "*$" modifier', () => {
|
||||
it('should parse the display text correctly and apply abstract static css style', () => {
|
||||
const str = 'name String*$';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('name String');
|
||||
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has static abstract "$*" modifier', () => {
|
||||
it('should parse the display text correctly and apply abstract static css style', () => {
|
||||
const str = 'name String$*';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('name String');
|
||||
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
it('should handle abstract and static combined (*$) on methods', () => {
|
||||
const str = 'getTime()*$';
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('getTime()');
|
||||
expect(details.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
it('should handle static and abstract combined ($*) on methods', () => {
|
||||
const str = 'getTime()$*';
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('getTime()');
|
||||
expect(details.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
it('should handle abstract and static combined (*$) on attributes', () => {
|
||||
const str = 'data String*$';
|
||||
const classMember = new ClassMember(str, 'attribute');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('data String');
|
||||
expect(details.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
it('should handle static and abstract combined ($*) on attributes', () => {
|
||||
const str = 'data String$*';
|
||||
const classMember = new ClassMember(str, 'attribute');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('data String');
|
||||
expect(details.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
it('should handle complex method with abstract static combination', () => {
|
||||
const str = '+processData(Map~String, List~Integer~~) Optional~Result~*$';
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe(
|
||||
'+processData(Map~String, List<Integer~>) : Optional<Result>'
|
||||
);
|
||||
expect(details.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
it('should handle attribute with visibility and abstract static combination', () => {
|
||||
const str = '#config Settings$*';
|
||||
const classMember = new ClassMember(str, 'attribute');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('#config Settings');
|
||||
expect(details.cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
// Verify existing classifier functionality still works
|
||||
it('should still handle single static classifier correctly', () => {
|
||||
const str = 'getName()$';
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('getName()');
|
||||
expect(details.cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should still handle single abstract classifier correctly', () => {
|
||||
const str = 'name String*';
|
||||
const classMember = new ClassMember(str, 'attribute');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('name String');
|
||||
expect(details.cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
|
||||
it('should handle empty classifier correctly', () => {
|
||||
const str = 'getValue()';
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
const details = classMember.getDisplayDetails();
|
||||
|
||||
expect(details.displayText).toBe('getValue()');
|
||||
expect(details.cssStyle).toBe('');
|
||||
});
|
||||
});
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTime()$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTime()*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract static classifier', function () {
|
||||
const str = `getTime()*$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for static abstract classifier', function () {
|
||||
const str = `getTime()$*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,664 +4,9 @@ const spyOn = vi.spyOn;
|
||||
|
||||
const staticCssStyle = 'text-decoration:underline;';
|
||||
const abstractCssStyle = 'font-style:italic;';
|
||||
const abstractStaticCssStyle = 'text-decoration:underline;font-style:italic;';
|
||||
|
||||
describe('given text representing a method, ', function () {
|
||||
describe('when method has no parameters', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTime()`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTime()`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTime()');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTime()`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTime()');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTime()`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTime()');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTime()`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTime()');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTime()$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTime()*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method has single parameter value', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTime(int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTime(int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(int)');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTime(int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(int)');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTime(int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(int)');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTime(int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(int)');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTime(int)$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTime(int)*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method has single parameter type and name (type first)', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTime(int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTime(int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(int count)');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTime(int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(int count)');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTime(int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(int count)');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTime(int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(int count)');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTime(int count)$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTime(int count)*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method has single parameter type and name (name first)', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTime(count int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTime(count int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(count int)');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTime(count int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(count int)');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTime(count int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(count int)');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTime(count int)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(count int)');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTime(count int)$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTime(count int)*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method has multiple parameters', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTime(string text, int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTime(string text, int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTime(string text, int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTime(string text, int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTime(string text, int count)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(str);
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTime(string text, int count)$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTime(string text, int count)*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method has return type', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTime() DateTime`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTime() DateTime`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTime() : DateTime');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTime() DateTime`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTime() : DateTime');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTime() DateTime`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTime() : DateTime');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTime() DateTime`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTime() : DateTime');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTime() DateTime$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTime() DateTime*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method parameter is generic', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTimes(List~T~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTimes(List~T~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes(List<T>)');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTimes(List~T~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes(List<T>)');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTimes(List~T~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes(List<T>)');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTimes(List~T~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes(List<T>)');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTimes(List~T~)$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTimes(List~T~)*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method parameter contains two generic', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTimes(List~T~, List~OT~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTimes(List~T~, List~OT~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes(List<T>, List<OT>)');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTimes(List~T~, List~OT~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes(List<T>, List<OT>)');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTimes(List~T~, List~OT~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes(List<T>, List<OT>)');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTimes(List~T~, List~OT~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes(List<T>, List<OT>)');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTimes(List~T~, List~OT~)$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTimes(List~T~, List~OT~)*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method parameter is a nested generic', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTimetableList(List~List~T~~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTimetableList(List~List~T~~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTimetableList(List<List<T>>)');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTimetableList(List~List~T~~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTimetableList(List<List<T>>)');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTimetableList(List~List~T~~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTimetableList(List<List<T>>)');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTimetableList(List~List~T~~)`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTimetableList(List<List<T>>)');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTimetableList(List~List~T~~)$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTimetableList(List~List~T~~)*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method parameter is a composite generic', function () {
|
||||
const methodNameAndParameters = 'getTimes(List~K, V~)';
|
||||
const expectedMethodNameAndParameters = 'getTimes(List<K, V>)';
|
||||
it('should parse correctly', function () {
|
||||
const str = methodNameAndParameters;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = '+' + methodNameAndParameters;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'+' + expectedMethodNameAndParameters
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = '-' + methodNameAndParameters;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'-' + expectedMethodNameAndParameters
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = '#' + methodNameAndParameters;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'#' + expectedMethodNameAndParameters
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = '~' + methodNameAndParameters;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'~' + expectedMethodNameAndParameters
|
||||
);
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = methodNameAndParameters + '$';
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = methodNameAndParameters + '*';
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method return type is generic', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTimes() List~T~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTimes() List~T~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes() : List<T>');
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTimes() List~T~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes() : List<T>');
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTimes() List~T~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes() : List<T>');
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTimes() List~T~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes() : List<T>');
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTimes() List~T~$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTimes() List~T~*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when method return type is a nested generic', function () {
|
||||
it('should parse correctly', function () {
|
||||
const str = `getTimetableList() List~List~T~~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'getTimetableList() : List<List<T>>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle public visibility', function () {
|
||||
const str = `+getTimetableList() List~List~T~~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'+getTimetableList() : List<List<T>>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle private visibility', function () {
|
||||
const str = `-getTimetableList() List~List~T~~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'-getTimetableList() : List<List<T>>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle protected visibility', function () {
|
||||
const str = `#getTimetableList() List~List~T~~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'#getTimetableList() : List<List<T>>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle internal visibility', function () {
|
||||
const str = `~getTimetableList() List~List~T~~`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'~getTimetableList() : List<List<T>>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should return correct css for static classifier', function () {
|
||||
const str = `getTimetableList() List~List~T~~$`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'getTimetableList() : List<List<T>>'
|
||||
);
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
|
||||
it('should return correct css for abstract classifier', function () {
|
||||
const str = `getTimetableList() List~List~T~~*`;
|
||||
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe(
|
||||
'getTimetableList() : List<List<T>>'
|
||||
);
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('--uncategorized tests--', function () {
|
||||
it('member name should handle double colons', function () {
|
||||
const str = `std::map ~int,string~ pMap;`;
|
||||
@@ -680,83 +25,82 @@ describe('given text representing a method, ', function () {
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('given text representing an attribute', () => {
|
||||
describe('when the attribute has no modifiers', () => {
|
||||
it('should parse the display text correctly', () => {
|
||||
const str = 'name String';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('name String');
|
||||
expect(displayDetails.cssStyle).toBe('');
|
||||
describe('Edge Cases and Additional Scenarios', () => {
|
||||
it('should handle method with special characters in name', function () {
|
||||
const str = `operator++(int value)`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('operator++(int value)');
|
||||
expect(classMember.id).toBe('operator++');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has public "+" modifier', () => {
|
||||
it('should parse the display text correctly', () => {
|
||||
const str = '+name String';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('+name String');
|
||||
expect(displayDetails.cssStyle).toBe('');
|
||||
it('should handle method with numbers in name', function () {
|
||||
const str = `method123(param)`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('method123(param)');
|
||||
expect(classMember.id).toBe('method123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has protected "#" modifier', () => {
|
||||
it('should parse the display text correctly', () => {
|
||||
const str = '#name String';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('#name String');
|
||||
expect(displayDetails.cssStyle).toBe('');
|
||||
it('should handle method with underscores and hyphens', function () {
|
||||
const str = `get_user_data(user_id int)`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('get_user_data(user_id int)');
|
||||
expect(classMember.id).toBe('get_user_data');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has private "-" modifier', () => {
|
||||
it('should parse the display text correctly', () => {
|
||||
const str = '-name String';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('-name String');
|
||||
expect(displayDetails.cssStyle).toBe('');
|
||||
it('should handle method with no spaces around parentheses', function () {
|
||||
const str = `method(param)`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('method(param)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has internal "~" modifier', () => {
|
||||
it('should parse the display text correctly', () => {
|
||||
const str = '~name String';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('~name String');
|
||||
expect(displayDetails.cssStyle).toBe('');
|
||||
it('should handle method with array parameters', function () {
|
||||
const str = `processArray(int[] numbers)`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('processArray(int[] numbers)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has static "$" modifier', () => {
|
||||
it('should parse the display text correctly and apply static css style', () => {
|
||||
const str = 'name String$';
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
|
||||
expect(displayDetails.displayText).toBe('name String');
|
||||
expect(displayDetails.cssStyle).toBe(staticCssStyle);
|
||||
it('should handle method with function pointer parameter', function () {
|
||||
const str = `callback(void (*fn)(int))`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('callback(void (*fn)(int))');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the attribute has abstract "*" modifier', () => {
|
||||
it('should parse the display text correctly and apply abstract css style', () => {
|
||||
const str = 'name String*';
|
||||
it('should handle method with complex nested generics (HTML encoded)', function () {
|
||||
const str = `process(Map<String, List<Map<Integer, String>>> data)`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
// Current behavior: parseGenericTypes converts < > to HTML entities
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('process(Map>> data)');
|
||||
});
|
||||
|
||||
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
|
||||
it('should handle method with colon in return type', function () {
|
||||
const str = `getNamespace() std::string`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('getNamespace() : std::string');
|
||||
});
|
||||
|
||||
expect(displayDetails.displayText).toBe('name String');
|
||||
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
|
||||
it('should handle malformed input gracefully - no parentheses', function () {
|
||||
const str = `not_a_method_missing_parentheses`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
// This will not match the method regex, so should handle gracefully
|
||||
// But currently throws when parseGenericTypes gets undefined
|
||||
expect(() => classMember.getDisplayDetails()).toThrow();
|
||||
});
|
||||
|
||||
it('should handle empty parameter list with classifier', function () {
|
||||
const str = `emptyMethod()$*`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('emptyMethod()');
|
||||
expect(classMember.getDisplayDetails().cssStyle).toBe(
|
||||
'text-decoration:underline;font-style:italic;'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle method with constructor-like name', function () {
|
||||
const str = `Class()`;
|
||||
const classMember = new ClassMember(str, 'method');
|
||||
expect(classMember.getDisplayDetails().displayText).toBe('Class()');
|
||||
expect(classMember.id).toBe('Class');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,64 +81,42 @@ export class ClassMember {
|
||||
let potentialClassifier = '';
|
||||
|
||||
if (this.memberType === 'method') {
|
||||
const methodRegEx = /([#+~-])?(.+)\((.*)\)([\s$*])?(.*)([$*])?/;
|
||||
const methodRegEx = /([#+~-])?(.+)\((.*)\)([$*]{0,2})(.*?)([$*]{0,2})$/;
|
||||
const match = methodRegEx.exec(input);
|
||||
if (match) {
|
||||
const detectedVisibility = match[1] ? match[1].trim() : '';
|
||||
|
||||
if (visibilityValues.includes(detectedVisibility)) {
|
||||
this.visibility = detectedVisibility as Visibility;
|
||||
}
|
||||
|
||||
this.id = match[2];
|
||||
this.visibility = (match[1] ? match[1].trim() : '') as Visibility;
|
||||
this.id = match[2].trim();
|
||||
this.parameters = match[3] ? match[3].trim() : '';
|
||||
potentialClassifier = match[4] ? match[4].trim() : '';
|
||||
this.returnType = match[5] ? match[5].trim() : '';
|
||||
|
||||
if (potentialClassifier === '') {
|
||||
const lastChar = this.returnType.substring(this.returnType.length - 1);
|
||||
if (/[$*]/.exec(lastChar)) {
|
||||
potentialClassifier = lastChar;
|
||||
this.returnType = this.returnType.substring(0, this.returnType.length - 1);
|
||||
}
|
||||
potentialClassifier = match[6] ? match[6].trim() : '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const length = input.length;
|
||||
const firstChar = input.substring(0, 1);
|
||||
const lastChar = input.substring(length - 1);
|
||||
const fieldRegEx = /([#+~-])?(.*?)([$*]{0,2})$/;
|
||||
const match = fieldRegEx.exec(input);
|
||||
|
||||
if (visibilityValues.includes(firstChar)) {
|
||||
this.visibility = firstChar as Visibility;
|
||||
if (match) {
|
||||
this.visibility = (match[1] ? match[1].trim() : '') as Visibility;
|
||||
this.id = match[2] ? match[2].trim() : '';
|
||||
potentialClassifier = match[3] ? match[3].trim() : '';
|
||||
}
|
||||
|
||||
if (/[$*]/.exec(lastChar)) {
|
||||
potentialClassifier = lastChar;
|
||||
}
|
||||
|
||||
this.id = input.substring(
|
||||
this.visibility === '' ? 0 : 1,
|
||||
potentialClassifier === '' ? length : length - 1
|
||||
);
|
||||
}
|
||||
|
||||
this.classifier = potentialClassifier;
|
||||
// Preserve one space only
|
||||
this.id = this.id.startsWith(' ') ? ' ' + this.id.trim() : this.id.trim();
|
||||
|
||||
const combinedText = `${this.visibility ? '\\' + this.visibility : ''}${parseGenericTypes(this.id)}${this.memberType === 'method' ? `(${parseGenericTypes(this.parameters)})${this.returnType ? ' : ' + parseGenericTypes(this.returnType) : ''}` : ''}`;
|
||||
this.text = combinedText.replaceAll('<', '<').replaceAll('>', '>');
|
||||
if (this.text.startsWith('\\<')) {
|
||||
this.text = this.text.replace('\\<', '~');
|
||||
}
|
||||
}
|
||||
|
||||
parseClassifier() {
|
||||
switch (this.classifier) {
|
||||
case '*':
|
||||
return 'font-style:italic;';
|
||||
case '$':
|
||||
return 'text-decoration:underline;';
|
||||
case '*':
|
||||
return 'font-style:italic;';
|
||||
case '$*':
|
||||
case '*$':
|
||||
return 'text-decoration:underline;font-style:italic;';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -82,10 +82,8 @@ Function arguments are optional: 'call <callback_name>()' simply executes 'callb
|
||||
<class-body><<EOF>> return "EOF_IN_STRUCT";
|
||||
<class-body>"[*]" { return 'EDGE_STATE';}
|
||||
<class-body>[{] return "OPEN_IN_STRUCT";
|
||||
<class-body>"<<" return 'ANNOTATION_START';
|
||||
<class-body>">>" return 'ANNOTATION_END';
|
||||
<class-body>[\n] /* nothing */
|
||||
<class-body>[^{}<>\n]* { return "MEMBER";}
|
||||
<class-body>[^{}\n]* { return "MEMBER";}
|
||||
|
||||
<*>"cssClass" return 'CSSCLASS';
|
||||
<*>"callback" return 'CALLBACK';
|
||||
@@ -297,21 +295,11 @@ classStatement
|
||||
| classIdentifier STRUCT_START members STRUCT_STOP {yy.addMembers($1,$3);}
|
||||
| classIdentifier STRUCT_START STRUCT_STOP {}
|
||||
| classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START members STRUCT_STOP {yy.setCssClass($1, $3);yy.addMembers($1,$5);}
|
||||
| classIdentifier STRUCT_START NEWLINE annotationList members STRUCT_STOP {for(const annotation of $4) { yy.addAnnotation($1, annotation); } yy.addMembers($1,$5);}
|
||||
| classIdentifier STRUCT_START NEWLINE annotationList STRUCT_STOP {for(const annotation of $4) { yy.addAnnotation($1, annotation); }}
|
||||
| classIdentifier STRUCT_START annotationList members STRUCT_STOP {for(const annotation of $3) { yy.addAnnotation($1, annotation); } yy.addMembers($1,$4);}
|
||||
| classIdentifier STRUCT_START annotationList STRUCT_STOP {for(const annotation of $3) { yy.addAnnotation($1, annotation); }}
|
||||
| classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START NEWLINE annotationList members STRUCT_STOP {yy.setCssClass($1, $3); for(const annotation of $6) { yy.addAnnotation($1, annotation); } yy.addMembers($1,$7);}
|
||||
| classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START NEWLINE annotationList STRUCT_STOP {yy.setCssClass($1, $3); for(const annotation of $6) { yy.addAnnotation($1, annotation); }}
|
||||
| classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START annotationList members STRUCT_STOP {yy.setCssClass($1, $3); for(const annotation of $5) { yy.addAnnotation($1, annotation); } yy.addMembers($1,$6);}
|
||||
| classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START annotationList STRUCT_STOP {yy.setCssClass($1, $3); for(const annotation of $5) { yy.addAnnotation($1, annotation); }}
|
||||
;
|
||||
|
||||
classIdentifier
|
||||
: CLASS className {$$=$2; yy.addClass($2);}
|
||||
| CLASS className classLabel {$$=$2; yy.addClass($2);yy.setClassLabel($2, $3);}
|
||||
| CLASS className annotationList {$$=$2; yy.addClass($2); for(const annotation of $3) { yy.addAnnotation($2, annotation); }}
|
||||
| CLASS className classLabel annotationList {$$=$2; yy.addClass($2);yy.setClassLabel($2, $3); for(const annotation of $4) { yy.addAnnotation($2, annotation); }}
|
||||
;
|
||||
|
||||
|
||||
@@ -322,12 +310,7 @@ emptyBody
|
||||
;
|
||||
|
||||
annotationStatement
|
||||
: annotationList className { for(const annotation of $1) { yy.addAnnotation($2, annotation); } }
|
||||
;
|
||||
|
||||
annotationList
|
||||
: ANNOTATION_START alphaNumToken ANNOTATION_END { $$ = [$2]; }
|
||||
| annotationList ANNOTATION_START alphaNumToken ANNOTATION_END { $1.push($3); $$ = $1; }
|
||||
: ANNOTATION_START alphaNumToken ANNOTATION_END className { yy.addAnnotation($4,$2); }
|
||||
;
|
||||
|
||||
members
|
||||
|
||||
@@ -36,36 +36,8 @@ export async function textHelper<T extends SVGGraphicsElement>(
|
||||
|
||||
annotationGroup = shapeSvg.insert('g').attr('class', 'annotation-group text');
|
||||
if (node.annotations.length > 0) {
|
||||
let yOffset = 0;
|
||||
const annotationElements: any[] = [];
|
||||
let maxAnnotationWidth = 0;
|
||||
|
||||
// First pass: create all annotation elements and find the maximum width
|
||||
for (const annotation of node.annotations) {
|
||||
const annotationEl = annotationGroup.insert('g').attr('class', 'annotation-text');
|
||||
const height = await addText(
|
||||
annotationEl,
|
||||
{ text: `«${annotation}»` } as unknown as ClassMember,
|
||||
0
|
||||
);
|
||||
const annotationBBox = annotationEl.node()!.getBBox();
|
||||
|
||||
annotationElements.push({
|
||||
element: annotationEl,
|
||||
width: annotationBBox.width,
|
||||
height: height,
|
||||
yOffset: yOffset,
|
||||
});
|
||||
|
||||
maxAnnotationWidth = Math.max(maxAnnotationWidth, annotationBBox.width);
|
||||
yOffset += height + TEXT_PADDING;
|
||||
}
|
||||
|
||||
// Second pass: center each annotation relative to the maximum width
|
||||
for (const annotation of annotationElements) {
|
||||
const centerOffset = (maxAnnotationWidth - annotation.width) / 2;
|
||||
annotation.element.attr('transform', `translate(${centerOffset}, ${annotation.yOffset})`);
|
||||
}
|
||||
const annotation = node.annotations[0];
|
||||
await addText(annotationGroup, { text: `«${annotation}»` } as unknown as ClassMember, 0);
|
||||
|
||||
const annotationGroupBBox = annotationGroup.node()!.getBBox();
|
||||
annotationGroupHeight = annotationGroupBBox.height;
|
||||
|
||||
@@ -16,7 +16,7 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
const svgWidth = bitWidth * bitsPerRow + 2;
|
||||
const svg: SVG = selectSvgElement(id);
|
||||
|
||||
svg.attr('viewbox', `0 0 ${svgWidth} ${svgHeight}`);
|
||||
svg.attr('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
|
||||
configureSvgSize(svg, svgHeight, svgWidth, config.useMaxWidth);
|
||||
|
||||
for (const [word, packet] of words.entries()) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Diagram } from '../../Diagram.js';
|
||||
import type { RadarDiagramConfig } from '../../config.type.js';
|
||||
import type { DiagramRenderer, DrawDefinition, SVG, SVGGroup } from '../../diagram-api/types.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import type { RadarDB, RadarAxis, RadarCurve } from './types.js';
|
||||
|
||||
const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
@@ -53,11 +54,9 @@ const drawFrame = (svg: SVG, config: Required<RadarDiagramConfig>): SVGGroup =>
|
||||
x: config.marginLeft + config.width / 2,
|
||||
y: config.marginTop + config.height / 2,
|
||||
};
|
||||
// Initialize the SVG
|
||||
svg
|
||||
.attr('viewbox', `0 0 ${totalWidth} ${totalHeight}`)
|
||||
.attr('width', totalWidth)
|
||||
.attr('height', totalHeight);
|
||||
configureSvgSize(svg, totalHeight, totalWidth, config.useMaxWidth ?? true);
|
||||
|
||||
svg.attr('viewBox', `0 0 ${totalWidth} ${totalHeight}`);
|
||||
// g element to center the radar chart
|
||||
return svg.append('g').attr('transform', `translate(${center.x}, ${center.y})`);
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ classDiagram
|
||||
UML provides mechanisms to represent class members, such as attributes and methods, and additional information about them.
|
||||
A single instance of a class in the diagram contains three compartments:
|
||||
|
||||
- The top compartment contains the name of the class. It is printed in bold and centered, and the first letter is capitalized. It may also contain optional annotation/stereotype text describing the nature of the class.
|
||||
- The top compartment contains the name of the class. It is printed in bold and centered, and the first letter is capitalized. It may also contain optional annotation text describing the nature of the class.
|
||||
- The middle compartment contains the attributes of the class. They are left-aligned and the first letter is lowercase.
|
||||
- The bottom compartment contains the operations the class can execute. They are also left-aligned and the first letter is lowercase.
|
||||
|
||||
@@ -175,6 +175,7 @@ To describe the visibility (or encapsulation) of an attribute or method/function
|
||||
>
|
||||
> - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*`
|
||||
> - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$`
|
||||
> - `$*` OR `*$` Both e.g: `someAbstractStaticMethod()$*` or `someAbstractStaticMethod() int$*`
|
||||
|
||||
> _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end:
|
||||
>
|
||||
@@ -349,7 +350,7 @@ classDiagram
|
||||
Galaxy --> "many" Star : Contains
|
||||
```
|
||||
|
||||
## Annotations on classes (stereotypes)
|
||||
## Annotations on classes
|
||||
|
||||
It is possible to annotate classes with markers to provide additional metadata about the class. This can give a clearer indication about its nature. Some common annotations include:
|
||||
|
||||
@@ -358,22 +359,38 @@ It is possible to annotate classes with markers to provide additional metadata a
|
||||
- `<<Service>>` To represent a service class
|
||||
- `<<Enumeration>>` To represent an enum
|
||||
|
||||
Annotations are defined within the opening `<<` and closing `>>`. There are multiple ways to add an annotation to a class, and any of them will output the same result:
|
||||
Annotations are defined within the opening `<<` and closing `>>`. There are two ways to add an annotation to a class, and either way the output will be same:
|
||||
|
||||
- **Inline with the class definition** (Recommended for consistency):
|
||||
> **Tip:**
|
||||
> In Mermaid class diagrams, annotations like `<<interface>>` can be attached in two ways:
|
||||
>
|
||||
> - **Inline with the class definition** (Recommended for consistency):
|
||||
>
|
||||
> ```mermaid-example
|
||||
> classDiagram
|
||||
> class Shape <<interface>>
|
||||
> ```
|
||||
>
|
||||
> - **Separate line after the class definition**:
|
||||
>
|
||||
> ```mermaid-example
|
||||
> classDiagram
|
||||
> class Shape
|
||||
> <<interface>> Shape
|
||||
> ```
|
||||
>
|
||||
> Both methods are fully supported and produce identical diagrams.
|
||||
> However, it is recommended to use the **inline style** for better readability and consistent formatting across diagrams.
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape <<interface>>
|
||||
```
|
||||
- In a **_separate line_** after a class is defined:
|
||||
|
||||
- **Separate line after the class definition**:
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape
|
||||
<<interface>> Shape
|
||||
```
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape
|
||||
<<interface>> Shape
|
||||
Shape : noOfVertices
|
||||
Shape : draw()
|
||||
```
|
||||
|
||||
- In a **_nested structure_** along with the class definition:
|
||||
|
||||
@@ -395,19 +412,6 @@ class Color{
|
||||
|
||||
```
|
||||
|
||||
For the case of multiple annotations the syntax is similar, you can add other annotations on the same line, or in a nested class definition add an annotation on a new line, as follows:
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class Shape{
|
||||
<<interface>>
|
||||
<<injected>>
|
||||
noOfVertices
|
||||
draw()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
||||
Comments can be entered within a class diagram, which will be ignored by the parser. Comments need to be on their own line, and must be prefaced with `%%` (double percent signs). Any text until the next newline will be treated as a comment, including any class diagram syntax.
|
||||
|
||||
Reference in New Issue
Block a user