diff --git a/packages/merman/src/state/diagram.test.ts b/packages/merman/src/state/diagram.test.ts index 1d73b1901308..25f65af38b15 100644 --- a/packages/merman/src/state/diagram.test.ts +++ b/packages/merman/src/state/diagram.test.ts @@ -274,6 +274,34 @@ ${ids expect(renderStateDiagram(source, { layoutMaxWidth: 1 })).toBe(renderStateDiagram(source)) }) + test.each([ + ["TB", 120], + ["LR", 40], + ] as const)( + "renders same-rank transitions between nested composite states from %s at %d columns", + (direction, width) => { + const diagram = parseMermaidStateDiagram(`stateDiagram-v2 +direction ${direction} +A --> B +A --> C +state D { + state E { + B --> F + } + B --> C +}`) + + const drawing = createStateDiagramDrawing(diagram, { layoutMaxWidth: width }) + const output = drawing.grid.toString({ + trimTop: true, + trimBottom: true, + }) + + expect(drawing.diagram.direction).toBe("TB") + for (const state of ["A", "B", "C", "D", "E", "F"]) expect(output).toContain(state) + }, + ) + test("preserves horizontal layouts that fit or have no finite width target", () => { const source = `stateDiagram-v2 direction LR diff --git a/packages/merman/src/state/routing.ts b/packages/merman/src/state/routing.ts index 04612445e5d8..ace2df1bab30 100644 --- a/packages/merman/src/state/routing.ts +++ b/packages/merman/src/state/routing.ts @@ -430,6 +430,15 @@ export function createStateTransitionRoutePlans( const targetIsHiddenMarker = isHiddenCompositeMarker(targetState) const base = { transition, from, to, targetIsChoice, targetIsHiddenMarker } const sideParallel = (): StateTransitionRoutePlan => { + if (from.centerY === to.centerY) { + const railY = allocateBottomRail() + return { + ...base, + kind: "bottom-parallel", + railY, + approachX: bottomApproachX(diagram, transition, from, to, bounds, railY), + } + } const railX = allocateSideRail(transition) return { ...base, @@ -787,7 +796,7 @@ function addSideParallelTransition(builder: StateTransitionRenderBuilder): void addRightDeparture(builder, from) addHorizontalLine(builder, startX, railX - 1, startY, 1) addCell(builder, { x: railX, y: startY, char: verticalStep === 1 ? "╮" : "╯" }) - for (let y = startY + verticalStep; y !== endY; y += verticalStep) addCell(builder, { x: railX, y, char: "│" }) + addVerticalLine(builder, railX, startY + verticalStep, endY - verticalStep, verticalStep) addCell(builder, { x: railX, y: endY, char: verticalStep === 1 ? "╯" : "╮" }) if (targetApproach) { const targetX = innerConnectorX(to, from.centerX)