diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js new file mode 100644 index 000000000..1f625e2e9 --- /dev/null +++ b/cypress/integration/rendering/erDiagram.spec.js @@ -0,0 +1,92 @@ +/* eslint-env jest */ +import { imgSnapshotTest } from '../../helpers/util'; + +describe('Entity Relationship Diagram', () => { + it('should render a simple ER diagram', () => { + imgSnapshotTest( + ` + erDiagram + CUSTOMER !-?< ORDER : places + ORDER !-!< LINE-ITEM : contains + `, + {logLevel : 1} + ); + cy.get('svg'); + }); + + it('should render an ER diagram with a recursive relationship', () => { + imgSnapshotTest( + ` + erDiagram + CUSTOMER !-?< CUSTOMER : refers + CUSTOMER !-?< ORDER : places + ORDER !-!< LINE-ITEM : contains + `, + {logLevel : 1} + ); + cy.get('svg'); + }); + + it('should render an ER diagram with multiple relationships between the same two entities', () => { + imgSnapshotTest( + ` + erDiagram + CUSTOMER !-!< ADDRESS : "invoiced at" + CUSTOMER !-!< ADDRESS : "receives goods at" + `, + {logLevel : 1} + ); + cy.get('svg'); + }); + + it('should render a cyclical ER diagram', () => { + imgSnapshotTest( + ` + erDiagram + A !-!< B : likes + B !-!< C : likes + C !-!< A : likes + `, + {logLevel : 1} + ); + cy.get('svg'); + + }); + + it('should render a not-so-simple ER diagram', () => { + imgSnapshotTest( + ` + erDiagram + DELIVERY-ADDRESS !-?< ORDER : receives + CUSTOMER >!-!< DELIVERY-ADDRESS : has + CUSTOMER !-?< ORDER : places + CUSTOMER !-?< INVOICE : "liable for" + INVOICE !-!< ORDER : covers + ORDER !-!< ORDER-ITEM : includes + PRODUCT-CATEGORY !-!< PRODUCT : contains + PRODUCT !-?< ORDER-ITEM : "ordered in" + `, + {logLevel : 1} + ); + cy.get('svg'); + }); + + it('should render multiple ER diagrams', () => { + imgSnapshotTest( + [ + ` + erDiagram + CUSTOMER !-?< ORDER : places + ORDER !-!< LINE-ITEM : contains + `, + ` + erDiagram + CUSTOMER !-?< ORDER : places + ORDER !-!< LINE-ITEM : contains + ` + ], + {logLevel : 1} + ); + cy.get('svg'); + }); +}); diff --git a/docs/entityRelationshipDiagram.md b/docs/entityRelationshipDiagram.md index cce0e5702..6b5e46bb3 100644 --- a/docs/entityRelationshipDiagram.md +++ b/docs/entityRelationshipDiagram.md @@ -2,6 +2,8 @@ > An entity–relationship model (or ER model) describes interrelated things of interest in a specific domain of knowledge. A basic ER model is composed of entity types (which classify the things of interest) and specifies relationships that can exist between entities (instances of those entity types). Wikipedia. +Note that practitioners of ER modelling almost always refer to entity types simply as entities. For example the CUSTOMER entity type would be referred to simply as the CUSTOMER entity. This is so common it would be inadvisable to do anything else, but technically an entity is an abstract instance of an entity type, and this is what an ER diagram shows - abstract instances, and the relationships between them. This is why entities are always named using singular nouns. + Mermaid can render ER diagrams ``` erDiagram @@ -14,6 +16,14 @@ erDiagram ORDER !-!< LINE-ITEM : contains ``` +Entity names are often capitalised, although there is no accepted standard on this, and it is not required in Mermaid. + +Relationships between entities are represented by lines with end markers representing cardinality. Mermaid uses the most popular crow's foot notation. The crow's foot intuitively conveys the possibility of many instances of the entity that it connects to. + +## Status + +ER diagrams are a new feature in Mermaid and are **experimental**. There are likely to be a few bugs and constraints, and enhancements will be made in due course. + ## Syntax ### Entities and Relationships diff --git a/src/diagrams/er/erRenderer.js b/src/diagrams/er/erRenderer.js index 1ae5a8d6a..3f37fd296 100644 --- a/src/diagrams/er/erRenderer.js +++ b/src/diagrams/er/erRenderer.js @@ -86,14 +86,16 @@ const drawEntities = function(svgNode, entities, graph) { const adjustEntities = function(svgNode, graph) { graph.nodes().forEach(function(v) { if (typeof v !== 'undefined' && typeof graph.node(v) !== 'undefined') { - d3.select('#' + v).attr( - 'transform', - 'translate(' + - (graph.node(v).x - graph.node(v).width / 2) + - ',' + - (graph.node(v).y - graph.node(v).height / 2) + - ' )' - ); + svgNode + .select('#' + v) + .attr( + 'transform', + 'translate(' + + (graph.node(v).x - graph.node(v).width / 2) + + ',' + + (graph.node(v).y - graph.node(v).height / 2) + + ' )' + ); } }); return; @@ -356,6 +358,7 @@ export const draw = function(text, id) { marginx: 20, marginy: 20, nodesep: 100, + edgesep: 100, ranksep: 100 }) .setDefaultEdgeLabel(function() {