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
3 changes: 2 additions & 1 deletion src/d2/_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ def render(
*,
layout: Literal["dagre", "elk", "tala"] | None = None,
theme: int | None = None,
target: str = "",
) -> bytes:
command = [find_d2()]
if layout is not None:
command += ["--layout", layout]
if theme is not None:
command += ["--theme", str(theme)]
command += ["--stdout-format", format, "-", "-"]
command += ["--target", target, "--stdout-format", format, "-", "-"]

result = subprocess.run(
command, input=str(diagram).encode(), capture_output=True, check=False
Expand Down
48 changes: 36 additions & 12 deletions src/d2/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,52 @@

RESERVED_KEYWORDS = frozenset(
{
"shape",
"style",
"label",
"near",
"layers",
"scenarios",
"steps",
"vars",
"classes",
"class",
"direction",
"shape",
"icon",
"link",
"constraint",
"tooltip",
"link",
"near",
"width",
"height",
"constraint",
"direction",
"top",
"left",
"grid-rows",
"grid-columns",
"grid-gap",
"vertical-gap",
"horizontal-gap",
"class",
"vars",
"style",
"source-arrowhead",
"target-arrowhead",
"classes",
"opacity",
"stroke",
"fill",
"fill-pattern",
"stroke-width",
"stroke-dash",
"border-radius",
"font",
"font-size",
"font-color",
"bold",
"italic",
"underline",
"text-transform",
"shadow",
"multiple",
"double-border",
"3d",
"animated",
"filled",
"layers",
"scenarios",
"steps",
}
)

Expand Down
4 changes: 2 additions & 2 deletions src/d2/style.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
from typing import Literal

from d2.helpers import add_label_and_properties
from d2.helpers import add_label_and_properties, quote_label

if sys.version_info >= (3, 12):
from typing import override
Expand All @@ -21,7 +21,7 @@ def render_value(value: str | float | bool) -> str | None:
if isinstance(value, bool):
return stringify_bool(value)
if isinstance(value, str):
return value if value != "" else None
return quote_label(value) if value != "" else None
return str(value)


Expand Down
4 changes: 4 additions & 0 deletions tests/test_d2/test_d2_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,7 @@ def test_d2_style_shape_options() -> None:
"}",
]
assert str(style) == "\n".join(expected_lines)


def test_d2_style_quotes_hex_colors() -> None:
assert str(Style(fill="#dbeafe")) == 'style: {\n fill: "#dbeafe"\n}'
5 changes: 5 additions & 0 deletions tests/test_d2/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,8 @@ def test_quote_label_keeps_already_quoted_labels() -> None:

def test_add_label_and_properties_quotes_label() -> None:
assert add_label_and_properties("a", "x: y", []) == ['a: "x: y"']


def test_quote_name_quotes_style_keywords() -> None:
assert quote_name("shadow") == '"shadow"'
assert quote_name("Fill") == '"Fill"'
20 changes: 20 additions & 0 deletions tests/test_d2/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,23 @@ def test_render__txt() -> None:
def test_render__invalid_source_raises_d2_error() -> None:
with pytest.raises(d2.D2Error):
d2.render("a -> {")


@requires_d2
def test_render__boards_renders_root_or_target() -> None:
diagram = d2.Diagram(
shapes=[d2.Shape(name="root_shape")],
layers=[
d2.Layer(name="detail", diagram=d2.Diagram(shapes=[d2.Shape(name="inner")]))
],
)
assert b"root_shape" in d2.render(diagram, "txt")
assert b"inner" in d2.render(diagram, "txt", target="layers.detail")


@requires_d2
def test_render__hex_colors_and_keyword_names() -> None:
diagram = d2.Diagram(
shapes=[d2.Shape(name="shadow", style=d2.Style(fill="#dbeafe"))]
)
assert d2.render(diagram).startswith(b"<?xml")
Loading