Skip to content

Commit

Permalink
d2js: updating target default + errors, readme
Browse files Browse the repository at this point in the history
  • Loading branch information
x-delfino committed Feb 24, 2025
1 parent 1417ded commit 28e498a
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 9 deletions.
2 changes: 1 addition & 1 deletion ci/release/changelogs/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#### Improvements 🧹

- d2js: Support `d2-config`. Support additional render options: [#2343](https://github.com/terrastruct/d2/pull/2343)
- d2js: Support `d2-config`. Support additional options: [#2343](https://github.com/terrastruct/d2/pull/2343)
- `themeID`
- `darkThemeID`
- `center`
Expand Down
19 changes: 12 additions & 7 deletions d2js/d2wasm/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,27 +271,34 @@ func Render(args []js.Value) (interface{}, error) {
return nil, &WASMError{Message: "missing 'diagram' field in input JSON", Code: 400}
}

animateInterval := 0
if input.Opts != nil && input.Opts.AnimateInterval != nil && *input.Opts.AnimateInterval > 0 {
animateInterval = int(*input.Opts.AnimateInterval)
}

var boardPath []string
var noChildren bool
noChildren := true

if input.Opts.Target != nil {
switch *input.Opts.Target {
case "*":
noChildren = false
case "":
noChildren = true
default:
target := *input.Opts.Target
if strings.HasSuffix(target, ".*") {
target = target[:len(target)-2]
} else {
noChildren = true
noChildren = false
}
key, err := d2parser.ParseKey(target)
if err != nil {
return nil, &WASMError{Message: fmt.Sprintf("target '%s' not recognized", target), Code: 400}
}
boardPath = key.StringIDA()
}
if !noChildren && animateInterval <= 0 {
return nil, &WASMError{Message: fmt.Sprintf("target '%s' only supported for animated SVGs", *input.Opts.Target), Code: 500}
}
}

diagram := input.Diagram.GetBoard(boardPath)
Expand All @@ -310,9 +317,7 @@ func Render(args []js.Value) (interface{}, error) {
renderOpts.Salt = input.Opts.Salt
}

var animateInterval = 0
if input.Opts != nil && input.Opts.AnimateInterval != nil && *input.Opts.AnimateInterval > 0 {
animateInterval = int(*input.Opts.AnimateInterval)
if animateInterval > 0 {
masterID, err := diagram.HashID(renderOpts.Salt)
if err != nil {
return nil, &WASMError{Message: fmt.Sprintf("cannot process animate interval: %s", err.Error()), Code: 500}
Expand Down
2 changes: 1 addition & 1 deletion d2js/js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ All [RenderOptions](#renderoptions) properties in addition to:
- `pad`: Pixels padded around the rendered diagram [default: 100]
- `scale`: Scale the output. E.g., 0.5 to halve the default size. The default will render SVG's that will fit to screen. Setting to 1 turns off SVG fitting to screen.
- `forceAppendix`: Adds an appendix for tooltips and links [default: false]
- `target`: Target board to render. Pass an empty string to target root board. If target ends with '*', it will be rendered with all of its scenarios, steps, and layers. Otherwise, only the target board will be rendered. E.g. --target='' to render root board only or --target='layers.x.*' to render layer 'x' with all of its children.
- `target`: Target board/s to render. If target ends with '*', it will be rendered with all of its scenarios, steps, and layers. Otherwise, only the target board will be rendered. Pass '*' to render all scenarios, steps, and layers. E.g. `target: 'layers.x.*'` to render layer 'x' with all of its children. Multi-board outputs are currently only supported for animated SVGs and so `animateInterval` must be set to a value greater than 0.
- `animateInterval`: If given, multiple boards are packaged as 1 SVG which transitions through each board at the interval (in milliseconds).
- `salt`: Add a salt value to ensure the output uses unique IDs. This is useful when generating multiple identical diagrams to be included in the same HTML doc, so that duplicate IDs do not cause invalid HTML. The salt value is a string that will be appended to IDs in the output.
- `noXMLTag`: Omit XML tag `(<?xml ...?>)` from output SVG files. Useful when generating SVGs for direct HTML embedding.
Expand Down
39 changes: 39 additions & 0 deletions d2js/js/test/unit/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,24 @@ x -> y
await d2.worker.terminate();
}, 20000);

test("animated multi-board works", async () => {
const d2 = new D2();
const source = `
x -> y
layers: {
numbers: {
1 -> 2
}
}
`;
const options = { target: "*", animateInterval: 1000 };
const result = await d2.compile(source, options);
const svg = await d2.render(result.diagram, result.renderOptions);
expect(svg).toContain("<svg");
expect(svg).toContain("</svg>");
await d2.worker.terminate();
}, 20000);

test("latex works", async () => {
const d2 = new D2();
const result = await d2.compile("x: |latex \\frac{f(x+h)-f(x)}{h} |");
Expand All @@ -141,4 +159,25 @@ x -> y
}
await d2.worker.terminate();
}, 20000);

test("handles unanimated multi-board error correctly", async () => {
const d2 = new D2();
const source = `
x -> y
layers: {
numbers: {
1 -> 2
}
}
`;
const result = await d2.compile(source);
try {
await d2.render(result.diagram, { target: "*" });
throw new Error("Should have thrown compile error");
} catch (err) {
expect(err).toBeDefined();
expect(err.message).not.toContain("Should have thrown compile error");
}
await d2.worker.terminate();
}, 20000);
});

0 comments on commit 28e498a

Please sign in to comment.