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

bug: State diagram fix classes type #3798

25 changes: 17 additions & 8 deletions packages/mermaid/src/diagrams/state/stateDb.js
Expand Up @@ -31,9 +31,20 @@ const FILL_KEYWORD = 'fill';
const BG_FILL = 'bgFill';
const STYLECLASS_SEP = ',';

/**
* Returns a new list of classes.
* In the future, this can be replaced with a class common to all diagrams.
* ClassDef information = { id: id, styles: [], textStyles: [] }
*
* @returns {{}}
*/
function newClassesList() {
return {};
}

let direction = DEFAULT_DIAGRAM_DIRECTION;
let rootDoc = [];
let classes = []; // style classes defined by a classDef
let classes = newClassesList(); // style classes defined by a classDef

const newDoc = () => {
return {
Expand Down Expand Up @@ -270,11 +281,9 @@ export const clear = function (saveCommon) {
};
currentDocument = documents.root;

currentDocument = documents.root;

// number of start and end nodes; used to construct ids
startEndCount = 0;
classes = [];
classes = newClassesList();
if (!saveCommon) {
commonClear();
}
Expand All @@ -300,7 +309,7 @@ export const getRelations = function () {
* else return the given id
*
* @param {string} id
* @returns {{id: string, type: string}} - the id and type that should be used
* @returns {string} - the id (original or constructed)
*/
function startIdIfNeeded(id = '') {
let fixedId = id;
Expand Down Expand Up @@ -329,7 +338,7 @@ function startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
* else return the given id
*
* @param {string} id
* @returns {{id: string, type: string}} - the id and type that should be used
* @returns {string} - the id (original or constructed)
*/
function endIdIfNeeded(id = '') {
let fixedId = id;
Expand Down Expand Up @@ -442,12 +451,12 @@ const getDividerId = () => {
* @example classDef my-style fill:#f96;
*
* @param {string} id - the id of this (style) class
* @param {string} styleAttributes - the string with 1 or more style attributes (each separated by a comma)
* @param {string | null} styleAttributes - the string with 1 or more style attributes (each separated by a comma)
*/
export const addStyleClass = function (id, styleAttributes = '') {
// create a new style class object with this id
if (typeof classes[id] === 'undefined') {
classes[id] = { id: id, styles: [], textStyles: [] };
classes[id] = { id: id, styles: [], textStyles: [] }; // This is a classDef
}
const foundClass = classes[id];
if (typeof styleAttributes !== 'undefined') {
Expand Down
Expand Up @@ -3,6 +3,7 @@ import stateDb from './stateDb';
import stateDiagram from './parser/stateDiagram.jison';

describe('state diagram V2, ', function () {
// TODO - these examples should be put into ./parser/stateDiagram.spec.js
describe('when parsing an info graph it', function () {
beforeEach(function () {
parser.yy = stateDb;
Expand Down
43 changes: 17 additions & 26 deletions packages/mermaid/src/diagrams/state/stateRenderer-v2.js
Expand Up @@ -58,11 +58,6 @@ const G_EDGE_LABELTYPE = 'text';
const G_EDGE_THICKNESS = 'normal';

// --------------------------------------
// When information is parsed and processed (extracted) by stateDb.extract()
// These are globals so the information can be accessed as needed (e.g. in setUpNode, etc.)
let diagramStates = [];
let diagramClasses = [];

// List of nodes created from the parsed diagram statement items
let nodeDb = {};

Expand All @@ -81,33 +76,28 @@ export const setConf = function (cnf) {
};

/**
* Returns the all the styles from classDef statements in the graph definition.
* Returns the all the classdef styles (a.k.a. classes) from classDef statements in the graph definition.
*
* @param {string} text - the diagram text to be parsed
* @param {Diagram} diagramObj
* @returns {object} ClassDef styles
* @param diagramObj
* @returns {object} ClassDef styles (a Map with keys = strings, values = )
*/
export const getClasses = function (text, diagramObj) {
log.trace('Extracting classes');
if (diagramClasses.length > 0) {
return diagramClasses; // we have already extracted the classes
}

diagramObj.db.clear();
try {
// Parse the graph definition
diagramObj.parser.parse(text);
// must run extract() to turn the parsed statements into states, relationships, classes, etc.
diagramObj.db.extract(diagramObj.db.getRootDocV2());

return diagramObj.db.getClasses();
} catch (e) {
return e;
}
};

/**
* Get classes from the db info item.
* Get classes from the db for the info item.
* If there aren't any or if dbInfoItem isn't defined, return an empty string.
* Else create 1 string from the list of classes found
*
Expand All @@ -132,7 +122,7 @@ function getClassesFromDbInfo(dbInfoItem) {
*
* @param itemId
* @param counter
* @param type
* @param {string | null} type
* @param typeSpacer
* @returns {string}
*/
Expand All @@ -147,10 +137,11 @@ export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOM
* @param g - graph
* @param {object} parent
* @param {object} parsedItem - parsed statement item
* @param {object[]} diagramStates - the list of all known states for the diagram
* @param {object} diagramDb
* @param {boolean} altFlag - for clusters, add the "statediagram-cluster-alt" CSS class
*/
const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) => {
const itemId = parsedItem.id;
const classStr = getClassesFromDbInfo(diagramStates[itemId]);

Expand Down Expand Up @@ -307,7 +298,7 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
}
if (parsedItem.doc) {
log.trace('Adding nodes children ');
setupDoc(g, parsedItem, parsedItem.doc, diagramDb, !altFlag);
setupDoc(g, parsedItem, parsedItem.doc, diagramStates, diagramDb, !altFlag);
}
};

Expand All @@ -318,25 +309,26 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
* @param g
* @param parentParsedItem - parsed Item that is the parent of this document (doc)
* @param doc - the document to set up
* @param {object} diagramStates - the list of all known states for the diagram
* @param diagramDb
* @param altFlag
* @param {boolean} altFlag
* @todo This duplicates some of what is done in stateDb.js extract method
*/
const setupDoc = (g, parentParsedItem, doc, diagramDb, altFlag) => {
const setupDoc = (g, parentParsedItem, doc, diagramStates, diagramDb, altFlag) => {
// graphItemCount = 0;
log.trace('items', doc);
doc.forEach((item) => {
switch (item.stmt) {
case STMT_STATE:
setupNode(g, parentParsedItem, item, diagramDb, altFlag);
setupNode(g, parentParsedItem, item, diagramStates, diagramDb, altFlag);
break;
case DEFAULT_STATE_TYPE:
setupNode(g, parentParsedItem, item, diagramDb, altFlag);
setupNode(g, parentParsedItem, item, diagramStates, diagramDb, altFlag);
break;
case STMT_RELATION:
{
setupNode(g, parentParsedItem, item.state1, diagramDb, altFlag);
setupNode(g, parentParsedItem, item.state2, diagramDb, altFlag);
setupNode(g, parentParsedItem, item.state1, diagramStates, diagramDb, altFlag);
setupNode(g, parentParsedItem, item.state2, diagramStates, diagramDb, altFlag);
const edgeData = {
id: 'edge' + graphItemCount,
arrowhead: 'normal',
Expand Down Expand Up @@ -407,8 +399,7 @@ export const draw = function (text, id, _version, diag) {
diag.db.extract(diag.db.getRootDocV2());
log.info(diag.db.getRootDocV2());

diagramStates = diag.db.getStates();
diagramClasses = diag.db.getClasses();
const diagramStates = diag.db.getStates();

// Create the input mermaid.graph
const g = new graphlib.Graph({
Expand All @@ -426,7 +417,7 @@ export const draw = function (text, id, _version, diag) {
return {};
});

setupNode(g, undefined, diag.db.getRootDocV2(), diag.db, true);
setupNode(g, undefined, diag.db.getRootDocV2(), diagramStates, diag.db, true);

// Set up an SVG group so that we can translate the final graph.
let sandboxElement;
Expand Down
31 changes: 31 additions & 0 deletions packages/mermaid/src/diagrams/state/stateRenderer-v2.spec.js
@@ -0,0 +1,31 @@
import { expectTypeOf } from 'vitest';

import { parser } from './parser/stateDiagram';
import stateDb from './stateDb';
import stateRendererV2 from './stateRenderer-v2';

// Can use this instead of having to register diagrams and load/orchestrate them, etc.
class FauxDiagramObj {
db = stateDb;
parser = parser;
renderer = stateRendererV2;

constructor(options = { db: stateDb, parser: parser, renderer: stateRendererV2 }) {
this.db = options.db;
this.parser = options.parser;
this.renderer = options.renderer;
this.parser.yy = this.db;
}
}

describe('stateRenderer-v2', () => {
describe('getClasses', () => {
const diagramText = 'statediagram-v2\n';
const fauxStateDiagram = new FauxDiagramObj();

it('returns a {}', () => {
const result = stateRendererV2.getClasses(diagramText, fauxStateDiagram);
expectTypeOf(result).toBeObject();
});
});
});
2 changes: 1 addition & 1 deletion packages/mermaid/src/mermaidAPI.ts
Expand Up @@ -32,7 +32,7 @@ import { evaluate } from './diagrams/common/common';
import { isEmpty } from 'lodash';

// diagram names that support classDef statements
const CLASSDEF_DIAGRAMS = ['graph', 'flowchart', 'flowchart-v2', 'stateDiagram'];
const CLASSDEF_DIAGRAMS = ['graph', 'flowchart', 'flowchart-v2', 'stateDiagram', 'stateDiagram-v2'];

const MAX_TEXTLENGTH_EXCEEDED_MSG =
'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
Expand Down