Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow to put notes in namespaces on classDiagram #5814

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/short-seals-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': minor
---

feat: allow to put notes in namespaces on classDiagram
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ USER 0:0
RUN corepack enable \
&& corepack enable pnpm

RUN apk add --no-cache git~=2.43.4 \
RUN apk add --no-cache git~=2.43 \
&& git config --add --system safe.directory /mermaid

ENV NODE_OPTIONS="--max_old_space_size=8192"
Expand Down
14 changes: 14 additions & 0 deletions cypress/integration/rendering/classDiagram-v2.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,20 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`
Expand Down
14 changes: 14 additions & 0 deletions cypress/integration/rendering/classDiagram-v3.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,20 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`
Expand Down
15 changes: 15 additions & 0 deletions demos/classchart.html
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ <h1>Class diagram demos</h1>
}
Admin --> Report : generates
</pre>
<hr />
<pre class="mermaid">
classDiagram
namespace Company.Project.Module {
Expand Down Expand Up @@ -240,6 +241,20 @@ <h1>Class diagram demos</h1>
Bike --> Square : "Logo Shape"

</pre>
<hr />
<pre class="mermaid">
classDiagram
note "This is a outer note"
note for Class1 "This is a outer note for Class1"
namespace ns {
note "This is a inner note"
note for Class1 "This is a inner note for Class1"
class Class1
class Class2
}
</pre>
<hr />

<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({
Expand Down
92 changes: 53 additions & 39 deletions packages/mermaid/src/diagrams/class/classDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ const MERMAID_DOM_ID_PREFIX = 'classId-';
let relations: ClassRelation[] = [];
let classes = new Map<string, ClassNode>();
const styleClasses = new Map<string, StyleClass>();
let notes: ClassNote[] = [];
let notes = new Map<string, ClassNote>();
let interfaces: Interface[] = [];
let classCounter = 0;
let namespaces = new Map<string, NamespaceNode>();
let namespaceCounter = 0;

let functions: any[] = [];
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
let functions: Function[] = [];

const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());

Expand Down Expand Up @@ -125,12 +126,12 @@ export const lookUpDomId = function (_id: string): string {

export const clear = function () {
relations = [];
classes = new Map();
notes = [];
classes = new Map<string, ClassNode>();
notes = new Map<string, ClassNote>();
interfaces = [];
functions = [];
functions.push(setupToolTips);
namespaces = new Map();
namespaces = new Map<string, NamespaceNode>();
namespaceCounter = 0;
direction = 'TB';
commonClear();
Expand All @@ -148,6 +149,11 @@ export const getRelations = function (): ClassRelation[] {
return relations;
};

export const getNote = function (id: string | number) {
const key = typeof id === 'number' ? `note${id}` : id;
return notes.get(key)!;
};

export const getNotes = function () {
return notes;
};
Expand Down Expand Up @@ -250,12 +256,15 @@ export const addMembers = function (className: string, members: string[]) {
};

export const addNote = function (text: string, className: string) {
const index = notes.size;
const note = {
id: `note${notes.length}`,
id: `note${index}`,
class: className,
text: text,
index: index,
};
notes.push(note);
notes.set(note.id, note);
return note.id;
};

export const cleanupLabel = function (label: string) {
Expand Down Expand Up @@ -501,6 +510,7 @@ export const addNamespace = function (id: string) {
namespaces.set(id, {
id: id,
classes: new Map(),
notes: new Map(),
children: {},
domId: MERMAID_DOM_ID_PREFIX + id + '-' + namespaceCounter,
} as NamespaceNode);
Expand All @@ -523,14 +533,24 @@ const getNamespaces = function (): NamespaceMap {
* @param classNames - Ids of the class to add
* @public
*/
export const addClassesToNamespace = function (id: string, classNames: string[]) {
export const addClassesToNamespace = function (
id: string,
classNames: string[],
noteNames: string[]
) {
if (!namespaces.has(id)) {
return;
}
for (const name of classNames) {
const { className } = splitClassNameAndType(name);
classes.get(className)!.parent = id;
namespaces.get(id)!.classes.set(className, classes.get(className)!);
const classNode = getClass(className);
classNode.parent = id;
namespaces.get(id)!.classes.set(className, classNode);
}
for (const noteName of noteNames) {
const noteNode = getNote(noteName);
noteNode.parent = id;
namespaces.get(id)!.notes.set(noteName, noteNode);
}
};

Expand Down Expand Up @@ -583,36 +603,28 @@ export const getData = () => {
const edges: Edge[] = [];
const config = getConfig();

for (const namespaceKey of namespaces.keys()) {
const namespace = namespaces.get(namespaceKey);
if (namespace) {
const node: Node = {
id: namespace.id,
label: namespace.id,
isGroup: true,
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: ['fill: none', 'stroke: black'],
look: config.look,
};
nodes.push(node);
}
for (const namespace of namespaces.values()) {
jgreywolf marked this conversation as resolved.
Show resolved Hide resolved
const node: Node = {
id: namespace.id,
label: namespace.id,
isGroup: true,
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: ['fill: none', 'stroke: black'],
look: config.look,
};
nodes.push(node);
}

for (const classKey of classes.keys()) {
const classNode = classes.get(classKey);
if (classNode) {
const node = classNode as unknown as Node;
node.parentId = classNode.parent;
node.look = config.look;
nodes.push(node);
}
for (const classNode of classes.values()) {
const node = classNode as unknown as Node;
node.parentId = classNode.parent;
node.look = config.look;
nodes.push(node);
}

let cnt = 0;
for (const note of notes) {
cnt++;
for (const note of notes.values()) {
const noteNode: Node = {
id: note.id,
label: note.text,
Expand All @@ -626,14 +638,15 @@ export const getData = () => {
`stroke: ${config.themeVariables.noteBorderColor}`,
],
look: config.look,
parentId: note.parent,
};
nodes.push(noteNode);

const noteClassId = classes.get(note.class)?.id ?? '';
const noteClassId = classes.get(note.class)?.id;

if (noteClassId) {
const edge: Edge = {
id: `edgeNote${cnt}`,
id: `edgeNote${note.index}`,
start: note.id,
end: noteClassId,
type: 'normal',
Expand Down Expand Up @@ -663,7 +676,7 @@ export const getData = () => {
nodes.push(interfaceNode);
}

cnt = 0;
let cnt = 0;
for (const classRelation of relations) {
cnt++;
const edge: Edge = {
Expand Down Expand Up @@ -705,6 +718,7 @@ export default {
clear,
getClass,
getClasses,
getNote,
getNotes,
addAnnotation,
addNote,
Expand Down
8 changes: 4 additions & 4 deletions packages/mermaid/src/diagrams/class/classDiagram.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ class C13["With Città foreign language"]
note "This is a keyword: ${keyword}. It truly is."
`;
parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
expect(classDb.getNote(0).text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
});

it.each(keywords)(
Expand All @@ -425,7 +425,7 @@ class C13["With Città foreign language"]
note "${keyword}"`;

parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`${keyword}`);
expect(classDb.getNote(0).text).toEqual(`${keyword}`);
}
);

Expand All @@ -439,7 +439,7 @@ class C13["With Città foreign language"]
`;

parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
expect(classDb.getNote(0).text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
});

it.each(keywords)(
Expand All @@ -454,7 +454,7 @@ class C13["With Città foreign language"]
`;

parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`${keyword}`);
expect(classDb.getNote(0).text).toEqual(`${keyword}`);
}
);

Expand Down
Loading
Loading