Skip to content

Commit

Permalink
Adds support for Mermaid diagrams in ADRs (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrwilson authored Oct 8, 2023
2 parents dbc14f2 + 259e0f9 commit 6543005
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Latest Version](https://img.shields.io/pypi/v/adr-viewer)](https://pypi.org/project/adr-viewer/)

Show off your Architecture Decision Records with an easy-to-navigate web page, either as a local web-server or generated static content.
Include diagrams in [Mermaid](https://mermaid.js.org) format using code blocks/fences, e.g. [test ADR 5](test/adr/0005-has-mermaid.md).

## Examples

Expand Down
3 changes: 3 additions & 0 deletions adr_viewer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def generate_content(path, template_dir_override=None, title=None) -> str:
config = {
"project_title": title if title else os.path.basename(os.getcwd()),
"records": [],
"include_mermaid": False,
}

for index, adr_file in enumerate(files):
Expand All @@ -29,6 +30,8 @@ def generate_content(path, template_dir_override=None, title=None) -> str:

if adr_attributes:
adr_attributes.index = index
if not config["include_mermaid"]:
config["include_mermaid"] = adr_attributes.includes_mermaid

config["records"].append(adr_attributes)
else:
Expand Down
10 changes: 9 additions & 1 deletion adr_viewer/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Adr:
status: str
body: str
index: int = 0
includes_mermaid: bool = False


def extract_statuses_from_adr(page_object) -> Iterator[str]:
Expand Down Expand Up @@ -52,7 +53,14 @@ def parse_adr(content: str) -> Optional[Adr]:

header = soup.find("h1")

includes_mermaid = soup.find(name="code", attrs={"class": "language-mermaid"})

if header:
return Adr(header.text, status, adr_as_html)
return Adr(
title=header.text,
status=status,
body=adr_as_html,
includes_mermaid=includes_mermaid,
)
else:
return None
32 changes: 31 additions & 1 deletion adr_viewer/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
{% if config.include_mermaid %}
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js" async></script>
{% endif %}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css">
<title>ADR Viewer - {{ config.project_title }}</title>

Expand Down Expand Up @@ -94,7 +97,34 @@ <h4 class="panel-title">
{% endfor %}
</div>
<footer>
Generated with <3 using ADR Viewer
Generated with &lt;3 using ADR Viewer
</footer>
{% if config.include_mermaid %}
<script type="application/javascript">
window.addEventListener("load", function(){
if (window.mermaid != undefined) {
mermaid.initialize({
startOnLoad: false,
securityLevel: 'loose',
logLevel: 'error',
wrap: true
});
{#
We have to hook into the Bootstrap events in the way the docs prescribe.
https://getbootstrap.com/docs/3.4/javascript/#collapse-events
#}
panels = document.querySelectorAll(".panel-collapse");
Array.from(panels).forEach(function(panel){
$(panel).on('show.bs.collapse', function(){
selector = "#" + panel.id + ' .language-mermaid';
mermaid.run({
querySelector: selector,
});
});
});
}
});
</script>
{% endif %}
</body>
</html>
14 changes: 14 additions & 0 deletions adr_viewer/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,17 @@ def test_should_ignore_invalid_files():
adr = parse_adr(markdown)

assert adr is None


def test_should_detect_mermaid():
markdown = open("test/adr/0005-has-mermaid.md").read()
adr = parse_adr(markdown)

assert adr.includes_mermaid


def test_should_not_detect_mermaid():
markdown = open("test/adr/0004-proposed-status.md").read()
adr = parse_adr(markdown)

assert not adr.includes_mermaid
12 changes: 12 additions & 0 deletions adr_viewer/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,15 @@ def test_should_render_html_with_collapsible_index():
html = render_html({"records": [adr]})

assert '<a data-toggle="collapse" href="#collapse123">Record 123</a>' in html


def test_should_render_html_with_mermaid():
html = render_html({"include_mermaid": True})

assert "mermaid.min.js" in html


def test_should_render_html_without_mermaid():
html = render_html({"include_mermaid": False})

assert "mermaid.min.js" not in html
82 changes: 82 additions & 0 deletions test/adr/0005-has-mermaid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# 2. Use mermaid for diagrams

Date: 2023-09-02

## Status

Accepted

## Context

We need to be able to diagram things.

## Decision

We will use [Mermaid](https://mermaid.js.org).

```mermaid
journey
title Using Mermaid in adr-viewer
section Writing ADR
Learn ADR: 5: Me
Write ADR: 3: Me
Render ADR: 6: Me
```

```mermaid
flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
```

```mermaid
sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!
```

```mermaid
classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}
```


## Consequences

We need to add a Mermaid script in the `head` tag of the template, potentially only when there is a Mermaid block in a Markdown document.

```mermaid
gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :after a1 , 20d
section Another
Task in sec :2014-01-12 , 12d
another task : 24d
```

0 comments on commit 6543005

Please sign in to comment.