Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ This type wraps some common style attributes, such as:

#### Styling nodes

In Mermaid, nodes can be through a style class definitions, which show up as `classDef` inside the flowchart.
In Mermaid, nodes can be styled using a style class definition, which show up as `classDef` inside the flowchart.
To do this, you must create a `NodeStyle` with a given name and a `StyleClass` and use that node style when creating a node.
If you create multiple nodes using the same node style, they will be grouped together automatically when applying the style class.

Expand Down Expand Up @@ -384,6 +384,51 @@ flowchart TD

```

#### Styling subgraphs

Subgraphs are nodes too, so they can be styled the same way with a `NodeStyle`.
Subgraphs and nodes can share the same node style, in which case they are grouped together automatically, just like nodes sharing a style.

```cs
using Mermaid.Flowcharts;
using Mermaid.Flowcharts.Nodes;
using Mermaid.Flowcharts.Subgraphs;

// Create a new flowchart
Flowchart flowchart = Flowchart.Create("Styling subgraphs");

// Define a style class
StyleClass subgraphStyleClass = new(Fill: new Fill(Color.FromHex("#9966ff")));

// Create a named node style
NodeStyle subgraphStyle = new("customStyle", subgraphStyleClass);

// Create a subgraph with the node style
Subgraph subgraph = Subgraph.Create<MermaidUnicodeText>("sg", "Subgraph", nodeStyle: subgraphStyle);

// Add the subgraph to the flowchart
flowchart.AddNode(subgraph);

// Generate the Mermaid output
string mermaid = flowchart.ToMermaidString();
```

Will generate the following Mermaid output:

```mermaid
---
title: "Styling subgraphs"
---
flowchart TD

subgraph sg ["Subgraph"]
end

classDef customStyle fill:#9966ff
class sg customStyle

```

#### Styling links

Styling links does not require a separate class, you directly apply a `StyleClass` to them.
Expand Down
4 changes: 2 additions & 2 deletions src/Mermaid.Flowcharts/Flowchart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ internal string ToMermaidStringWithOrderedLinks(int indentations, string indenta
flowchartStringBuilder.AppendLine(link.ToMermaidString(indentations + 1, indentationText));
}

// Group all node styles across flowchart and all subgraphs recursively together
// Group all node and subgraph styles across flowchart and all subgraphs recursively together
Dictionary<NodeStyle, HashSet<NodeIdentifier>> distinctNodeStyles = [];
foreach (Node node in AllNodes)
foreach (INode node in AllNodeChildren)
{
if (node.NodeStyle is null)
{
Expand Down
1 change: 1 addition & 0 deletions src/Mermaid.Flowcharts/Nodes/INode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace Mermaid.Flowcharts.Nodes;
public interface INode : IMermaidPrintable
{
public NodeIdentifier Id { get; }
public NodeStyle? NodeStyle { get; }
}

public interface INode<TNode> : INode, IEquatable<TNode>
Expand Down
28 changes: 15 additions & 13 deletions src/Mermaid.Flowcharts/Subgraphs/Subgraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,40 @@ public record Subgraph : INode<Subgraph>
public NodeIdentifier Id { get; }
public INodeText Title { get; }
public SubgraphDirection? Direction { get; }
public NodeStyle? NodeStyle { get; }
public IEnumerable<Node> Nodes => _nodes.OfType<Node>();
public IEnumerable<Subgraph> Subgraphs => _nodes.OfType<Subgraph>();
public IEnumerable<Link> Links => _links.AsReadOnly();

private Subgraph(NodeIdentifier id, INodeText title, SubgraphDirection? direction = null)
private Subgraph(NodeIdentifier id, INodeText title, SubgraphDirection? direction = null, NodeStyle? nodeStyle = null)
{
Id = id;
Title = title;
Direction = direction;
NodeStyle = nodeStyle;
}

public static Subgraph CreateNew<TNodeText>(string title, SubgraphDirection? direction = null)
public static Subgraph CreateNew<TNodeText>(string title, SubgraphDirection? direction = null, NodeStyle? nodeStyle = null)
where TNodeText : INodeText<TNodeText>
=> new(NodeIdentifier.Create(), TNodeText.FromString(title), direction);
=> new(NodeIdentifier.Create(), TNodeText.FromString(title), direction, nodeStyle);

public static Subgraph Create<TNodeText>(string identifier, string title, SubgraphDirection? direction = null)
public static Subgraph Create<TNodeText>(string identifier, string title, SubgraphDirection? direction = null, NodeStyle? nodeStyle = null)
where TNodeText : INodeText<TNodeText>
=> new(NodeIdentifier.FromString(identifier), TNodeText.FromString(title), direction);
=> new(NodeIdentifier.FromString(identifier), TNodeText.FromString(title), direction, nodeStyle);

public static Subgraph CreateNew<TNodeText>(TNodeText title, SubgraphDirection? direction = null)
public static Subgraph CreateNew<TNodeText>(TNodeText title, SubgraphDirection? direction = null, NodeStyle? nodeStyle = null)
where TNodeText : INodeText
=> new(NodeIdentifier.Create(), title, direction);
=> new(NodeIdentifier.Create(), title, direction, nodeStyle);

public static Subgraph Create<TNodeText>(string identifier, TNodeText title, SubgraphDirection? direction = null)
public static Subgraph Create<TNodeText>(string identifier, TNodeText title, SubgraphDirection? direction = null, NodeStyle? nodeStyle = null)
where TNodeText : INodeText
=> new(NodeIdentifier.FromString(identifier), title, direction);
=> new(NodeIdentifier.FromString(identifier), title, direction, nodeStyle);

public static Subgraph CreateNew(string title, SubgraphDirection? direction = null)
=> CreateNew<MermaidUnicodeText>(title, direction);
public static Subgraph CreateNew(string title, SubgraphDirection? direction = null, NodeStyle? nodeStyle = null)
=> CreateNew<MermaidUnicodeText>(title, direction, nodeStyle);

public static Subgraph Create(string identifier, string title, SubgraphDirection? direction = null)
=> Create<MermaidUnicodeText>(identifier, title, direction);
public static Subgraph Create(string identifier, string title, SubgraphDirection? direction = null, NodeStyle? nodeStyle = null)
=> Create<MermaidUnicodeText>(identifier, title, direction, nodeStyle);

public Subgraph AddNode(INode node)
{
Expand Down
108 changes: 108 additions & 0 deletions tests/Mermaid.Flowcharts.Tests/FlowchartTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,114 @@ class {node3Id},{node4Id} customStyle2
Assert.Equal(expected, actual);
}

[Fact]
public void Flowchart_WhenOneSubgraphOneStyle_ToMermaidString()
{
// Arrange
Flowchart flowchart = Flowchart.Create();
string subgraphId = Guid.NewGuid().ToString();
string subgraphTitle = Guid.NewGuid().ToString();
StyleClass subgraphStyleClass = new(Fill: new Fill(Color.FromHex("#ff9966")));
NodeStyle subgraphStyle = new("customStyle", subgraphStyleClass);
Subgraph subgraph = Subgraph.Create<MermaidUnicodeText>(subgraphId, subgraphTitle, nodeStyle: subgraphStyle);
flowchart.AddNode(subgraph);
string expected =
$"""
flowchart TD

subgraph {subgraphId} ["{subgraphTitle}"]
end

classDef customStyle fill:#ff9966
class {subgraphId} customStyle

""";

// Act
string actual = flowchart.ToMermaidString(0, " ");

// Assert
Assert.Equal(expected, actual);
}

[Fact]
public void Flowchart_WhenNestedSubgraphStyled_ToMermaidString()
{
// Arrange
Flowchart flowchart = Flowchart.Create();
string outerId = Guid.NewGuid().ToString();
string outerTitle = Guid.NewGuid().ToString();
Subgraph outer = Subgraph.Create<MermaidUnicodeText>(outerId, outerTitle);

string innerId = Guid.NewGuid().ToString();
string innerTitle = Guid.NewGuid().ToString();
StyleClass innerStyleClass = new(Fill: new Fill(Color.FromHex("#9966ff")));
NodeStyle innerStyle = new("customStyle", innerStyleClass);
Subgraph inner = Subgraph.Create<MermaidUnicodeText>(innerId, innerTitle, nodeStyle: innerStyle);

outer.AddNode(inner);
flowchart.AddNode(outer);
string expected =
$"""
flowchart TD

subgraph {outerId} ["{outerTitle}"]

subgraph {innerId} ["{innerTitle}"]
end
end

classDef customStyle fill:#9966ff
class {innerId} customStyle

""";

// Act
string actual = flowchart.ToMermaidString(0, " ");

// Assert
Assert.Equal(expected, actual);
}

[Fact]
public void Flowchart_WhenNodeAndSubgraphShareStyle_ToMermaidString()
{
// Arrange
Flowchart flowchart = Flowchart.Create();
StyleClass sharedStyleClass = new(Fill: new Fill(Color.FromHex("#ff9966")));
NodeStyle sharedStyle = new("customStyle", sharedStyleClass);

string nodeId = Guid.NewGuid().ToString();
string nodeText = Guid.NewGuid().ToString();
Node node = Node.Create<MermaidUnicodeText>(nodeId, nodeText, nodeStyle: sharedStyle);

string subgraphId = Guid.NewGuid().ToString();
string subgraphTitle = Guid.NewGuid().ToString();
Subgraph subgraph = Subgraph.Create<MermaidUnicodeText>(subgraphId, subgraphTitle, nodeStyle: sharedStyle);

flowchart
.AddNode(node)
.AddNode(subgraph);
string expected =
$"""
flowchart TD
{nodeId}["{nodeText}"]

subgraph {subgraphId} ["{subgraphTitle}"]
end

classDef customStyle fill:#ff9966
class {nodeId},{subgraphId} customStyle

""";

// Act
string actual = flowchart.ToMermaidString(0, " ");

// Assert
Assert.Equal(expected, actual);
}

[Fact]
public void Flowchart_WhenOneLinkOneStyle_ToMermaidString()
{
Expand Down
39 changes: 39 additions & 0 deletions tests/Mermaid.Flowcharts.Tests/ReadmeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,45 @@ class a,b customStyle
Assert.Equal(expected, mermaid);
}

[Fact]
public void StylingSubgraphs()
{
// Create a new flowchart
Flowchart flowchart = Flowchart.Create("Styling subgraphs");

// Define a style class
StyleClass subgraphStyleClass = new(Fill: new Fill(Color.FromHex("#9966ff")));

// Create a named node style
NodeStyle subgraphStyle = new("customStyle", subgraphStyleClass);

// Create a subgraph with the node style
Subgraph subgraph = Subgraph.Create<MermaidUnicodeText>("sg", "Subgraph", nodeStyle: subgraphStyle);

// Add the subgraph to the flowchart
flowchart.AddNode(subgraph);

// Generate the Mermaid output
string mermaid = flowchart.ToMermaidString();
string expected =
"""
---
title: "Styling subgraphs"
---
flowchart TD

subgraph sg ["Subgraph"]
end

classDef customStyle fill:#9966ff
class sg customStyle

""";

// Assert
Assert.Equal(expected, mermaid);
}

[Fact]
public void StylingLinks()
{
Expand Down