Compare commits

...

2 Commits

Author SHA1 Message Date
Sidharth Vinod
5482d0a603 Merge branch 'develop' into bug/2234_class-diagram-call-addClass-if-needed 2024-01-23 10:52:42 +05:30
Justin Greywolf
a22e4193c5 Handle non declared classes 2024-01-22 10:11:10 -08:00
2 changed files with 103 additions and 39 deletions

View File

@@ -116,8 +116,8 @@ export const clear = function () {
commonClear(); commonClear();
}; };
export const getClass = function (id: string): ClassNode { export const getClass = function (className: string): ClassNode {
return classes[id]; return classes[className];
}; };
export const getClasses = function (): ClassMap { export const getClasses = function (): ClassMap {
@@ -156,6 +156,7 @@ export const addRelation = function (relation: ClassRelation) {
* @public * @public
*/ */
export const addAnnotation = function (className: string, annotation: string) { export const addAnnotation = function (className: string, annotation: string) {
addClass(className);
const validatedClassName = splitClassNameAndType(className).className; const validatedClassName = splitClassNameAndType(className).className;
classes[validatedClassName].annotations.push(annotation); classes[validatedClassName].annotations.push(annotation);
}; };
@@ -199,6 +200,8 @@ export const addMembers = function (className: string, members: string[]) {
}; };
export const addNote = function (text: string, className: string) { export const addNote = function (text: string, className: string) {
addClass(className);
const note = { const note = {
id: `note${notes.length}`, id: `note${notes.length}`,
class: className, class: className,
@@ -217,17 +220,19 @@ export const cleanupLabel = function (label: string) {
/** /**
* Called by parser when assigning cssClass to a class * Called by parser when assigning cssClass to a class
* *
* @param ids - Comma separated list of ids * @param classNames - Comma separated list of ids
* @param className - Class to add * @param cssClass - Class to add
*/ */
export const setCssClass = function (ids: string, className: string) { export const setCssClass = function (classNames: string, cssClass: string) {
ids.split(',').forEach(function (_id) { classNames.split(',').forEach(function (_className) {
let id = _id; let className = _className;
if (_id[0].match(/\d/)) { addClass(className);
id = MERMAID_DOM_ID_PREFIX + id;
if (_className[0].match(/\d/)) {
className = MERMAID_DOM_ID_PREFIX + className;
} }
if (classes[id] !== undefined) { if (classes[className] !== undefined) {
classes[id].cssClasses.push(className); classes[className].cssClasses.push(cssClass);
} }
}); });
}; };
@@ -235,66 +240,73 @@ export const setCssClass = function (ids: string, className: string) {
/** /**
* Called by parser when a tooltip is found, e.g. a clickable element. * Called by parser when a tooltip is found, e.g. a clickable element.
* *
* @param ids - Comma separated list of ids * @param classNames - Comma separated list of ids
* @param tooltip - Tooltip to add * @param tooltip - Tooltip to add
*/ */
const setTooltip = function (ids: string, tooltip?: string) { const setTooltip = function (classNames: string, tooltip?: string) {
ids.split(',').forEach(function (id) { classNames.split(',').forEach(function (className) {
if (tooltip !== undefined) { if (tooltip !== undefined) {
classes[id].tooltip = sanitizeText(tooltip); addClass(className);
classes[className].tooltip = sanitizeText(tooltip);
} }
}); });
}; };
export const getTooltip = function (id: string, namespace?: string) { export const getTooltip = function (className: string, namespace?: string) {
if (namespace) { if (namespace) {
return namespaces[namespace].classes[id].tooltip; return namespaces[namespace].classes[className].tooltip;
} }
return classes[id].tooltip; return classes[className].tooltip;
}; };
/** /**
* Called by parser when a link is found. Adds the URL to the vertex data. * Called by parser when a link is found. Adds the URL to the vertex data.
* *
* @param ids - Comma separated list of ids * @param classNames - Comma separated list of class ids
* @param linkStr - URL to create a link for * @param linkStr - URL to create a link for
* @param target - Target of the link, _blank by default as originally defined in the svgDraw.js file * @param target - Target of the link, _blank by default as originally defined in the svgDraw.js file
*/ */
export const setLink = function (ids: string, linkStr: string, target: string) { export const setLink = function (classNames: string, linkStr: string, target: string) {
const config = getConfig(); const config = getConfig();
ids.split(',').forEach(function (_id) { classNames.split(',').forEach(function (_className) {
let id = _id; let className = _className;
if (_id[0].match(/\d/)) { if (_className[0].match(/\d/)) {
id = MERMAID_DOM_ID_PREFIX + id; className = MERMAID_DOM_ID_PREFIX + className;
} }
if (classes[id] !== undefined) { addClass(className);
classes[id].link = utils.formatUrl(linkStr, config); if (classes[className] !== undefined) {
classes[className].link = utils.formatUrl(linkStr, config);
if (config.securityLevel === 'sandbox') { if (config.securityLevel === 'sandbox') {
classes[id].linkTarget = '_top'; classes[className].linkTarget = '_top';
} else if (typeof target === 'string') { } else if (typeof target === 'string') {
classes[id].linkTarget = sanitizeText(target); classes[className].linkTarget = sanitizeText(target);
} else { } else {
classes[id].linkTarget = '_blank'; classes[className].linkTarget = '_blank';
} }
} }
}); });
setCssClass(ids, 'clickable'); setCssClass(classNames, 'clickable');
}; };
/** /**
* Called by parser when a click definition is found. Registers an event handler. * Called by parser when a click definition is found. Registers an event handler.
* *
* @param ids - Comma separated list of ids * @param classNames - Comma separated list of class ids
* @param functionName - Function to be called on click * @param functionName - Function to be called on click
* @param functionArgs - Function args the function should be called with * @param functionArgs - Function args the function should be called with
*/ */
export const setClickEvent = function (ids: string, functionName: string, functionArgs: string) { export const setClickEvent = function (
ids.split(',').forEach(function (id) { classNames: string,
setClickFunc(id, functionName, functionArgs); functionName: string,
classes[id].haveCallback = true; functionArgs: string
) {
classNames.split(',').forEach(function (className) {
addClass(className);
setClickFunc(className, functionName, functionArgs);
classes[className].haveCallback = true;
}); });
setCssClass(ids, 'clickable'); setCssClass(classNames, 'clickable');
}; };
const setClickFunc = function (_domId: string, functionName: string, functionArgs: string) { const setClickFunc = function (_domId: string, functionName: string, functionArgs: string) {
@@ -308,6 +320,7 @@ const setClickFunc = function (_domId: string, functionName: string, functionArg
} }
const id = domId; const id = domId;
addClass(id);
if (classes[id] !== undefined) { if (classes[id] !== undefined) {
const elemId = lookUpDomId(id); const elemId = lookUpDomId(id);
let argList: string[] = []; let argList: string[] = [];
@@ -447,9 +460,8 @@ const getNamespaces = function (): NamespaceMap {
* @public * @public
*/ */
export const addClassesToNamespace = function (id: string, classNames: string[]) { export const addClassesToNamespace = function (id: string, classNames: string[]) {
if (namespaces[id] === undefined) { addNamespace(id);
return;
}
for (const name of classNames) { for (const name of classNames) {
const { className } = splitClassNameAndType(name); const { className } = splitClassNameAndType(name);
classes[className].parent = id; classes[className].parent = id;
@@ -458,6 +470,7 @@ export const addClassesToNamespace = function (id: string, classNames: string[])
}; };
export const setCssStyle = function (id: string, styles: string[]) { export const setCssStyle = function (id: string, styles: string[]) {
addClass(id);
const thisClass = classes[id]; const thisClass = classes[id];
if (!styles || !thisClass) { if (!styles || !thisClass) {
return; return;

View File

@@ -258,9 +258,30 @@ class C13["With Città foreign language"]
expect(classDb.getClass('C13').label).toBe('With Città foreign language'); expect(classDb.getClass('C13').label).toBe('With Città foreign language');
}); });
it('should handle link if class not created first', function () {
const str = `classDiagram
link Class1 "/#anchor"`;
parser.parse(str);
const actual = parser.yy.getClass('Class1');
expect(actual.link).toBe('/#anchor');
});
it('should handle "note for" without pre-defining class', function () {
const str = `classDiagram
note for Class1 "test"`;
parser.parse(str);
const actual = parser.yy.getClass('Class1');
expect(classDb.getNotes()[0].text).toEqual(`test`);
});
it('should handle "note for"', function () { it('should handle "note for"', function () {
const str = 'classDiagram\n' + 'Class11 <|.. Class12\n' + 'note for Class11 "test"\n'; const str = 'classDiagram\n' + 'Class11 <|.. Class12\n' + 'note for Class11 "test"\n';
parser.parse(str); parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`test`);
}); });
it('should handle "note"', function () { it('should handle "note"', function () {
@@ -632,6 +653,16 @@ foo()
classDb.clear(); classDb.clear();
parser.yy = classDb; parser.yy = classDb;
}); });
it('should handle link if class not created first', function () {
const str = `classDiagram
link Class1 "/#anchor"`;
parser.parse(str);
const actual = parser.yy.getClass('Class1');
expect(actual.link).toBe('/#anchor');
});
it('should handle href link', function () { it('should handle href link', function () {
spyOn(classDb, 'setLink'); spyOn(classDb, 'setLink');
const str = 'classDiagram\n' + 'class Class1 \n' + 'click Class1 href "google.com" '; const str = 'classDiagram\n' + 'class Class1 \n' + 'click Class1 href "google.com" ';
@@ -690,6 +721,15 @@ foo()
expect(classDb.setClickEvent).toHaveBeenCalledWith('Class1', 'functionCall'); expect(classDb.setClickEvent).toHaveBeenCalledWith('Class1', 'functionCall');
}); });
it('should handle function call when class not created first', function () {
spyOn(classDb, 'setClickEvent');
const str = `classDiagram
click Class1 call functionCall()`;
parser.parse(str);
expect(classDb.setClickEvent).toHaveBeenCalledWith('Class1', 'functionCall');
});
it('should handle function call with tooltip', function () { it('should handle function call with tooltip', function () {
spyOn(classDb, 'setClickEvent'); spyOn(classDb, 'setClickEvent');
spyOn(classDb, 'setTooltip'); spyOn(classDb, 'setTooltip');
@@ -744,6 +784,17 @@ foo()
parser.yy = classDb; parser.yy = classDb;
}); });
it('should handle annotation if class not created first', function () {
const str = 'classDiagram\n' + '<<interface>> Class1';
parser.parse(str);
const actual = parser.yy.getClass('Class1');
expect(actual.annotations.length).toBe(1);
expect(actual.members.length).toBe(0);
expect(actual.methods.length).toBe(0);
expect(actual.annotations[0]).toBe('interface');
});
it('should handle class annotations', function () { it('should handle class annotations', function () {
const str = 'classDiagram\n' + 'class Class1\n' + '<<interface>> Class1'; const str = 'classDiagram\n' + 'class Class1\n' + '<<interface>> Class1';
parser.parse(str); parser.parse(str);