Merge pull request #3798 from weedySeaDragon/bug/1952-stateDiagram--classDef-fix-classes-type

bug: State diagram  fix classes type
This commit is contained in:
Sidharth Vinod 2022-11-17 22:01:09 +05:30 committed by GitHub
commit 461236030c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 35 deletions

View File

@ -31,9 +31,20 @@ const FILL_KEYWORD = 'fill';
const BG_FILL = 'bgFill'; const BG_FILL = 'bgFill';
const STYLECLASS_SEP = ','; 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 direction = DEFAULT_DIAGRAM_DIRECTION;
let rootDoc = []; let rootDoc = [];
let classes = []; // style classes defined by a classDef let classes = newClassesList(); // style classes defined by a classDef
const newDoc = () => { const newDoc = () => {
return { return {
@ -270,11 +281,9 @@ export const clear = function (saveCommon) {
}; };
currentDocument = documents.root; currentDocument = documents.root;
currentDocument = documents.root;
// number of start and end nodes; used to construct ids // number of start and end nodes; used to construct ids
startEndCount = 0; startEndCount = 0;
classes = []; classes = newClassesList();
if (!saveCommon) { if (!saveCommon) {
commonClear(); commonClear();
} }
@ -300,7 +309,7 @@ export const getRelations = function () {
* else return the given id * else return the given id
* *
* @param {string} 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 = '') { function startIdIfNeeded(id = '') {
let fixedId = id; let fixedId = id;
@ -329,7 +338,7 @@ function startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
* else return the given id * else return the given id
* *
* @param {string} 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 = '') { function endIdIfNeeded(id = '') {
let fixedId = id; let fixedId = id;
@ -442,12 +451,12 @@ const getDividerId = () => {
* @example classDef my-style fill:#f96; * @example classDef my-style fill:#f96;
* *
* @param {string} id - the id of this (style) class * @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 = '') { export const addStyleClass = function (id, styleAttributes = '') {
// create a new style class object with this id // create a new style class object with this id
if (typeof classes[id] === 'undefined') { 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]; const foundClass = classes[id];
if (typeof styleAttributes !== 'undefined') { if (typeof styleAttributes !== 'undefined') {

View File

@ -3,6 +3,7 @@ import stateDb from './stateDb';
import stateDiagram from './parser/stateDiagram.jison'; import stateDiagram from './parser/stateDiagram.jison';
describe('state diagram V2, ', function () { describe('state diagram V2, ', function () {
// TODO - these examples should be put into ./parser/stateDiagram.spec.js
describe('when parsing an info graph it', function () { describe('when parsing an info graph it', function () {
beforeEach(function () { beforeEach(function () {
parser.yy = stateDb; parser.yy = stateDb;

View File

@ -58,11 +58,6 @@ const G_EDGE_LABELTYPE = 'text';
const G_EDGE_THICKNESS = 'normal'; 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 // List of nodes created from the parsed diagram statement items
let nodeDb = {}; let nodeDb = {};
@ -81,25 +76,20 @@ 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 {string} text - the diagram text to be parsed
* @param {Diagram} diagramObj * @param diagramObj
* @returns {object} ClassDef styles * @returns {object} ClassDef styles (a Map with keys = strings, values = )
*/ */
export const getClasses = function (text, diagramObj) { export const getClasses = function (text, diagramObj) {
log.trace('Extracting classes'); log.trace('Extracting classes');
if (diagramClasses.length > 0) {
return diagramClasses; // we have already extracted the classes
}
diagramObj.db.clear(); diagramObj.db.clear();
try { try {
// Parse the graph definition // Parse the graph definition
diagramObj.parser.parse(text); diagramObj.parser.parse(text);
// must run extract() to turn the parsed statements into states, relationships, classes, etc. // must run extract() to turn the parsed statements into states, relationships, classes, etc.
diagramObj.db.extract(diagramObj.db.getRootDocV2()); diagramObj.db.extract(diagramObj.db.getRootDocV2());
return diagramObj.db.getClasses(); return diagramObj.db.getClasses();
} catch (e) { } catch (e) {
return e; return e;
@ -107,7 +97,7 @@ export const getClasses = function (text, diagramObj) {
}; };
/** /**
* 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. * 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 * Else create 1 string from the list of classes found
* *
@ -132,7 +122,7 @@ function getClassesFromDbInfo(dbInfoItem) {
* *
* @param itemId * @param itemId
* @param counter * @param counter
* @param type * @param {string | null} type
* @param typeSpacer * @param typeSpacer
* @returns {string} * @returns {string}
*/ */
@ -147,10 +137,11 @@ export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOM
* @param g - graph * @param g - graph
* @param {object} parent * @param {object} parent
* @param {object} parsedItem - parsed statement item * @param {object} parsedItem - parsed statement item
* @param {object[]} diagramStates - the list of all known states for the diagram
* @param {object} diagramDb * @param {object} diagramDb
* @param {boolean} altFlag - for clusters, add the "statediagram-cluster-alt" CSS class * @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 itemId = parsedItem.id;
const classStr = getClassesFromDbInfo(diagramStates[itemId]); const classStr = getClassesFromDbInfo(diagramStates[itemId]);
@ -307,7 +298,7 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
} }
if (parsedItem.doc) { if (parsedItem.doc) {
log.trace('Adding nodes children '); log.trace('Adding nodes children ');
setupDoc(g, parsedItem, parsedItem.doc, diagramDb, !altFlag); setupDoc(g, parsedItem, parsedItem.doc, diagramStates, diagramDb, !altFlag);
} }
}; };
@ -318,25 +309,26 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
* @param g * @param g
* @param parentParsedItem - parsed Item that is the parent of this document (doc) * @param parentParsedItem - parsed Item that is the parent of this document (doc)
* @param doc - the document to set up * @param doc - the document to set up
* @param {object} diagramStates - the list of all known states for the diagram
* @param diagramDb * @param diagramDb
* @param altFlag * @param {boolean} altFlag
* @todo This duplicates some of what is done in stateDb.js extract method * @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; // graphItemCount = 0;
log.trace('items', doc); log.trace('items', doc);
doc.forEach((item) => { doc.forEach((item) => {
switch (item.stmt) { switch (item.stmt) {
case STMT_STATE: case STMT_STATE:
setupNode(g, parentParsedItem, item, diagramDb, altFlag); setupNode(g, parentParsedItem, item, diagramStates, diagramDb, altFlag);
break; break;
case DEFAULT_STATE_TYPE: case DEFAULT_STATE_TYPE:
setupNode(g, parentParsedItem, item, diagramDb, altFlag); setupNode(g, parentParsedItem, item, diagramStates, diagramDb, altFlag);
break; break;
case STMT_RELATION: case STMT_RELATION:
{ {
setupNode(g, parentParsedItem, item.state1, diagramDb, altFlag); setupNode(g, parentParsedItem, item.state1, diagramStates, diagramDb, altFlag);
setupNode(g, parentParsedItem, item.state2, diagramDb, altFlag); setupNode(g, parentParsedItem, item.state2, diagramStates, diagramDb, altFlag);
const edgeData = { const edgeData = {
id: 'edge' + graphItemCount, id: 'edge' + graphItemCount,
arrowhead: 'normal', arrowhead: 'normal',
@ -407,8 +399,7 @@ export const draw = function (text, id, _version, diag) {
diag.db.extract(diag.db.getRootDocV2()); diag.db.extract(diag.db.getRootDocV2());
log.info(diag.db.getRootDocV2()); log.info(diag.db.getRootDocV2());
diagramStates = diag.db.getStates(); const diagramStates = diag.db.getStates();
diagramClasses = diag.db.getClasses();
// Create the input mermaid.graph // Create the input mermaid.graph
const g = new graphlib.Graph({ const g = new graphlib.Graph({
@ -426,7 +417,7 @@ export const draw = function (text, id, _version, diag) {
return {}; 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. // Set up an SVG group so that we can translate the final graph.
let sandboxElement; let sandboxElement;

View File

@ -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();
});
});
});

View File

@ -32,7 +32,7 @@ import { evaluate } from './diagrams/common/common';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
// diagram names that support classDef statements // 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 = const MAX_TEXTLENGTH_EXCEEDED_MSG =
'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa'; 'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';