Skip to content

Commit 721571f

Browse files
Copilotjongalloway
andcommitted
Fix title/node overlap in LayoutTimelineDiagram; update snapshot
Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
1 parent 88091e0 commit 721571f

3 files changed

Lines changed: 61 additions & 16 deletions

File tree

src/DiagramForge/Layout/DefaultLayoutEngine.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,14 @@ private static void LayoutTimelineDiagram(
325325
if (eventNodes.Count > 0)
326326
colWidth = Math.Max(colWidth, eventNodes.Max(n => n.Width));
327327

328+
// When a title is present, shift the first row down to clear it. The title
329+
// is rendered by SvgRenderer at y=(DiagramPadding - 4); it needs
330+
// (TitleFontSize + 8) of vertical room, matching the identical offset that
331+
// SvgRenderer.ComputeHeight already reserves at the canvas bottom.
332+
double titleOffset = !string.IsNullOrWhiteSpace(diagram.Title) ? theme.TitleFontSize + 8 : 0;
333+
328334
// Place period nodes in a single horizontal row.
329-
double periodY = pad;
335+
double periodY = pad + titleOffset;
330336
for (int i = 0; i < periodNodes.Count; i++)
331337
{
332338
var pn = periodNodes[i];

tests/DiagramForge.E2ETests/Fixtures/mermaid-timeline.expected.svg

Lines changed: 15 additions & 15 deletions
Loading

tests/DiagramForge.Tests/Parsers/Mermaid/MermaidTimelineParserTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,45 @@ public void Layout_AllPeriodNodes_HaveEqualWidth()
272272
Assert.All(periods, p => Assert.Equal(firstWidth, p.Width));
273273
}
274274

275+
[Fact]
276+
public void Layout_WithTitle_PeriodRowStartsBelowTitle()
277+
{
278+
// With a title the period row must be pushed down enough to clear the title
279+
// text. Without the offset the title (at y≈DiagramPadding-4) visually
280+
// overlaps the first node row (at y=DiagramPadding).
281+
const string withTitle = """
282+
timeline
283+
title Product Roadmap
284+
Q1 : Research
285+
""";
286+
287+
const string withoutTitle = """
288+
timeline
289+
Q1 : Research
290+
""";
291+
292+
var diagramWithTitle = _parser.Parse(withTitle);
293+
var diagramWithoutTitle = _parser.Parse(withoutTitle);
294+
295+
_layout.Layout(diagramWithTitle, _theme);
296+
_layout.Layout(diagramWithoutTitle, _theme);
297+
298+
double periodYWithTitle = diagramWithTitle.Nodes.Values
299+
.Single(n => n.Metadata.TryGetValue("timeline:kind", out var k) && k is "period").Y;
300+
301+
double periodYWithoutTitle = diagramWithoutTitle.Nodes.Values
302+
.Single(n => n.Metadata.TryGetValue("timeline:kind", out var k) && k is "period").Y;
303+
304+
// The titled diagram's period row must start strictly below the un-titled one.
305+
Assert.True(periodYWithTitle > periodYWithoutTitle,
306+
$"Period row with title ({periodYWithTitle}) should be below period row without title ({periodYWithoutTitle})");
307+
308+
// And the gap must be at least the title font size + a small margin.
309+
double gap = periodYWithTitle - periodYWithoutTitle;
310+
Assert.True(gap >= _theme.TitleFontSize,
311+
$"Gap ({gap}) should be >= TitleFontSize ({_theme.TitleFontSize}) to avoid title/node overlap");
312+
}
313+
275314
// ── DiagramType ───────────────────────────────────────────────────────────
276315

277316
[Fact]

0 commit comments

Comments
 (0)