Skip to content

fix(class): Self-referential class multiplicity labels rendered multi…#7578

Open
Gaston202 wants to merge 3 commits intomermaid-js:developfrom
Gaston202:bug/7560_class-diagram-self-ref-multiplicity
Open

fix(class): Self-referential class multiplicity labels rendered multi…#7578
Gaston202 wants to merge 3 commits intomermaid-js:developfrom
Gaston202:bug/7560_class-diagram-self-ref-multiplicity

Conversation

@Gaston202
Copy link
Copy Markdown
Contributor

@Gaston202 Gaston202 commented Apr 4, 2026

📑 Summary

Fix a bug where cardinality/multiplicity labels (e.g. "1", "0..1") were rendered three times on self-referential class diagram relationships.
Resolves #7560

📏 Design Decisions

The dagre layout algorithm handles self-loops by splitting them into 3 separate edge segments (outgoing, middle, returning). The existing code used structuredClone to copy all edge properties to
each segment, duplicating cardinality labels (startLabelRight, endLabelLeft) across all 3 edges instead of distributing them correctly.

The fix clears cardinality labels from cloned self-loop edges so each segment only carries its relevant label:

  • edge1 (outgoing): keeps startLabelRight, clears endLabelLeft
  • edgeMid (middle): clears both
  • edge2 (returning): clears startLabelRight, keeps endLabelLeft

Additionally, createLabel was appending elements to the wrong parent container in both dagre-wrapper/edges.js and rendering-elements/edges.js, which was fixed by removing redundant appendChild
calls.

📋 Tasks

Make sure you

  • 📖 have read the contribution guidelines
  • 💻 have added necessary unit/e2e tests.
  • 📓 have added documentation. Make sure MERMAID_RELEASE_VERSION is used for all new features.
  • 🦋 If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add
    features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

…ple times

Fixes mermaid-js#7560 where cardinality labels (e.g. "1", "0..1") were displayed 3x
on self-referential class diagram relationships.

Root cause: The dagre layout splits self-loops into 3 edges but
structuredClone copied cardinality labels to all of them. Now each
segment only carries its relevant cardinality label. Also fix DOM
hierarchy bug in edge label creation where labels were appended to
the wrong parent element.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 4, 2026

🦋 Changeset detected

Latest commit: 1f98db8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
mermaid Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 4, 2026

Deploy Preview for mermaid-js ready!

Name Link
🔨 Latest commit 1f98db8
🔍 Latest deploy log https://app.netlify.com/projects/mermaid-js/deploys/69d51e6999576300087c0897
😎 Deploy Preview https://deploy-preview-7578--mermaid-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions bot added the Type: Bug / Error Something isn't working or is incorrect label Apr 4, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 4, 2026

Open in StackBlitz

@mermaid-js/examples

npm i https://pkg.pr.new/@mermaid-js/examples@7578

mermaid

npm i https://pkg.pr.new/mermaid@7578

@mermaid-js/layout-elk

npm i https://pkg.pr.new/@mermaid-js/layout-elk@7578

@mermaid-js/layout-tidy-tree

npm i https://pkg.pr.new/@mermaid-js/layout-tidy-tree@7578

@mermaid-js/mermaid-zenuml

npm i https://pkg.pr.new/@mermaid-js/mermaid-zenuml@7578

@mermaid-js/parser

npm i https://pkg.pr.new/@mermaid-js/parser@7578

@mermaid-js/tiny

npm i https://pkg.pr.new/@mermaid-js/tiny@7578

commit: 1f98db8

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 4, 2026

Codecov Report

❌ Patch coverage is 0% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 3.33%. Comparing base (2b938d0) to head (1f98db8).
⚠️ Report is 69 commits behind head on develop.

Files with missing lines Patch % Lines
...rc/rendering-util/layout-algorithms/dagre/index.js 0.00% 10 Missing ⚠️
...ages/mermaid/src/dagre-wrapper/mermaid-graphlib.js 0.00% 9 Missing ⚠️
packages/mermaid/src/dagre-wrapper/edges.js 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           develop   #7578      +/-   ##
==========================================
- Coverage     3.34%   3.33%   -0.01%     
==========================================
  Files          524     535      +11     
  Lines        55392   56232     +840     
  Branches       795     820      +25     
==========================================
+ Hits          1853    1877      +24     
- Misses       53539   54355     +816     
Flag Coverage Δ
unit 3.33% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...aid/src/rendering-util/rendering-elements/edges.js 0.00% <ø> (ø)
packages/mermaid/src/dagre-wrapper/edges.js 0.00% <0.00%> (ø)
...ages/mermaid/src/dagre-wrapper/mermaid-graphlib.js 0.00% <0.00%> (ø)
...rc/rendering-util/layout-algorithms/dagre/index.js 0.00% <0.00%> (ø)

... and 24 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@argos-ci
Copy link
Copy Markdown

argos-ci bot commented Apr 4, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 29 changed, 1 added Apr 7, 2026, 3:22 PM

The middle edge segment of a self-loop should preserve its label
(e.g. "refers") — only the cardinality labels need to be cleared.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@knsv knsv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sisyphus-bot]

Review: fix(class): Self-referential class multiplicity labels rendered multiple times

Thanks for digging into this one, @Gaston202! Self-referential edge rendering is a tricky area with the 3-way edge splitting, and you've correctly identified both the root cause and the fix locations across the legacy and modern rendering paths. Nice work.


What's working well

🎉 [praise] Correct root cause identification — the structuredClone of the edge copies all label properties to all 3 sub-edges, causing 3x rendering. The fix in dagre/index.js correctly clears labels from the sub-edges that shouldn't carry them (edge1 keeps startLabelRight, edge2 keeps endLabelLeft, edgeMid gets neither).

🎉 [praise] Consistent fix across both dagre-wrapper/edges.js (deprecated path) and rendering-util/rendering-elements/edges.js (modern path) — good attention to ensuring both code paths are addressed.

🎉 [praise] The addition of edge2.arrowTypeStart = 'none' is a nice catch — without it, the intermediate-to-target sub-edge would render a spurious start arrowhead at the intermediate node.


Things to address

🟡 [important] Missing visual regression test. This is a rendering bug fix that affects shared layout code (rendering-util/). Per project conventions, renderer changes need E2E visual regression tests in cypress/integration/rendering/ using imgSnapshotTest(). A test like this would prevent future regressions:

imgSnapshotTest(
  `classDiagram
    class SelfReferential{
        +int id
        +SelfReferential referenced
    }
    SelfReferential "1" --> "0..1" SelfReferential : referenced
  `
);

There are currently no E2E tests covering self-referential class diagram edges with multiplicity labels, so this would be valuable new coverage. (cypress/integration/rendering/classDiagram*.spec.js would be the right home.)

🟡 [important] Missing changeset. This is a user-facing bug fix and needs a changeset for release notes. It would be great to run pnpm changeset, select @mermaid-js/mermaid, pick patch, and write something like fix: self-referential class multiplicity labels no longer rendered multiple times.

💡 [suggestion] Defensive clearing of all 4 label positions. The fix clears startLabelRight and endLabelLeft on the appropriate sub-edges, which covers class diagram multiplicity labels. But startLabelLeft and endLabelRight are not cleared — if any diagram type sets those on a self-referential edge, they'd still appear 3x. Worth defensively clearing all four label positions for completeness:

// dagre/index.js — cyclic edge splitting
edge1.endLabelLeft = '';
edge1.endLabelRight = '';     // defensive

edgeMid.startLabelRight = '';
edgeMid.startLabelLeft = '';  // defensive
edgeMid.endLabelLeft = '';
edgeMid.endLabelRight = '';   // defensive

edge2.startLabelRight = '';
edge2.startLabelLeft = '';    // defensive

🟢 [nit] The PR description body could link to the issue it fixes (e.g., Resolves #7560) so GitHub auto-links and auto-closes on merge.


Security

No XSS or injection issues identified. The changes are purely structural (DOM node parenting and property clearing on cloned edge objects) — no new user input flows, no changes to sanitization paths.


Self-check

  • At least one 🎉 praise item
  • No duplicate comments
  • Severity tally: 🔴 0 / 🟡 2 / 🟢 1 / 💡 1 / 🎉 3
  • Verdict matches criteria: COMMENT (2 🟡 items, no 🔴)
  • Not a draft → full review appropriate
  • No inline comments used
  • Tone check: collaborative and appreciative ✓

@knsv
Copy link
Copy Markdown
Collaborator

knsv commented Apr 7, 2026

Good work @Gaston202, let's get it over the finishing line!

Clear label props on split sub-edges to prevent multiplicity labels
from rendering 3x after structuredClone during layout.

- keep labels only on correct sub-edges
- defensively clear all label positions
- remove unintended arrow on edge2
- add visual regression test
- add changeset
@Gaston202
Copy link
Copy Markdown
Contributor Author

Thanks so much for the thorough and thoughtful review, @knsv!

I've just addressed all the feedback:

-Added visual regression test - Created a test in cypress/integration/rendering/classDiagram-v3.spec.js that specifically covers self-referential class diagrams with multiplicity labels to prevent future regressions.

-Added changeset - Created .changeset/fix-7560-self-referential-multiplicity.md with a clear description for the release notes.

-Defensive clearing - Extended the fix to clear all 4 label positions (startLabelLeft/Right, endLabelLeft/Right) on all three sub-edges in both rendering paths:

  • rendering-util/layout-algorithms/dagre/index.js (modern)
  • dagre-wrapper/mermaid-graphlib.js (legacy)

Really appreciate the positive feedback and the suggestion about defensive clearing. It's a small addition that makes the solution more robust for any future diagram types that might use the other label positions.

Thanks again for the great review! 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug / Error Something isn't working or is incorrect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Class diagrams: Self referential multiplicity is displayed multiple times

2 participants