From bb5a7a585edf4b4245ce18e05fd532ea7cf3aa55 Mon Sep 17 00:00:00 2001 From: Thomas Di Cizerone Date: Sun, 16 Mar 2025 22:04:30 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=97=EF=B8=8F=20Add=20radar=20renderer=20t?= =?UTF-8?q?ests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mermaid/src/diagrams/radar/radar.spec.ts | 105 ++++++++++++++++-- .../mermaid/src/diagrams/radar/renderer.ts | 12 +- packages/mermaid/src/diagrams/radar/styles.ts | 8 +- packages/mermaid/src/diagrams/radar/types.ts | 20 ++-- .../diagrams/sequence/sequenceDiagram.spec.js | 1 - 5 files changed, 124 insertions(+), 22 deletions(-) diff --git a/packages/mermaid/src/diagrams/radar/radar.spec.ts b/packages/mermaid/src/diagrams/radar/radar.spec.ts index 9971e8b17..5e5f444c6 100644 --- a/packages/mermaid/src/diagrams/radar/radar.spec.ts +++ b/packages/mermaid/src/diagrams/radar/radar.spec.ts @@ -1,6 +1,11 @@ import { it, describe, expect } from 'vitest'; import { db } from './db.js'; import { parser } from './parser.js'; +import { renderer, relativeRadius, closedRoundCurve } from './renderer.js'; +import { Diagram } from '../../Diagram.js'; +import mermaidAPI from '../../mermaidAPI.js'; +import { a } from 'vitest/dist/chunks/suite.qtkXWc6R.js'; +import { buildRadarStyleOptions } from './styles.js'; const { clear, @@ -135,10 +140,6 @@ describe('radar diagrams', () => { curve mycurve{1,2,3} `; await expect(parser.parse(str)).resolves.not.toThrow(); - - // TODO: ✨ Fix this test - // expect(getConfig().marginTop).toBe(80); - // expect(getConfig().axisLabelFactor).toBe(1.25); }); it('should parse radar diagram with theme override', async () => { @@ -149,8 +150,6 @@ describe('radar diagrams', () => { curve mycurve{1,2,3} `; await expect(parser.parse(str)).resolves.not.toThrow(); - - // TODO: ✨ Add tests for theme override }); it('should handle radar diagram with radar style override', async () => { @@ -161,7 +160,99 @@ describe('radar diagrams', () => { curve mycurve{1,2,3} `; await expect(parser.parse(str)).resolves.not.toThrow(); + }); - // TODO: ✨ Add tests for style override + describe('renderer', () => { + describe('relativeRadius', () => { + it('should calculate relative radius', () => { + expect(relativeRadius(5, 0, 10, 100)).toBe(50); + }); + + it('should handle min value', () => { + expect(relativeRadius(0, 0, 10, 100)).toBe(0); + }); + + it('should handle max value', () => { + expect(relativeRadius(10, 0, 10, 100)).toBe(100); + }); + + it('should clip values below min', () => { + expect(relativeRadius(-5, 0, 10, 100)).toBe(0); + }); + + it('should clip values above max', () => { + expect(relativeRadius(15, 0, 10, 100)).toBe(100); + }); + + it('should handle negative min', () => { + expect(relativeRadius(5, -10, 10, 100)).toBe(75); + }); + }); + + describe('closedRoundCurve', () => { + it('should construct a polygon if tension is 0', () => { + const points = [ + { x: 0, y: 0 }, + { x: 100, y: 0 }, + { x: 100, y: 100 }, + { x: 0, y: 100 }, + ]; + const tension = 0; + const path = closedRoundCurve(points, tension); + expect(path).toMatchInlineSnapshot( + `"M0,0 C0,0 100,0 100,0 C100,0 100,100 100,100 C100,100 0,100 0,100 C0,100 0,0 0,0 Z"` + ); + }); + + it('should construct a simple round curve', () => { + const points = [ + { x: 0, y: 0 }, + { x: 100, y: 100 }, + ]; + const tension = 0.5; + const path = closedRoundCurve(points, tension); + expect(path).toMatchInlineSnapshot(`"M0,0 C0,0 100,100 100,100 C100,100 0,0 0,0 Z"`); + }); + + it('should construct a closed round curve', () => { + const points = [ + { x: 0, y: 0 }, + { x: 100, y: 0 }, + { x: 100, y: 100 }, + { x: 0, y: 100 }, + ]; + const tension = 0.5; + const path = closedRoundCurve(points, tension); + expect(path).toMatchInlineSnapshot( + `"M0,0 C50,-50 50,-50 100,0 C150,50 150,50 100,100 C50,150 50,150 0,100 C-50,50 -50,50 0,0 Z"` + ); + }); + }); + + describe('draw', () => { + it('should draw a simple radar diagram', async () => { + const str = `radar-beta + axis A,B,C + curve mycurve{1,2,3}`; + await mermaidAPI.parse(str); + const diagram = await Diagram.fromText(str); + await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + }); + + it('should draw a complex radar diagram', async () => { + const str = `radar-beta + title Radar diagram + accTitle: Radar accTitle + accDescr: Radar accDescription + axis A["Axis A"], B["Axis B"] ,C["Axis C"] + curve mycurve["My Curve"]{1,2,3} + curve mycurve2["My Curve 2"]{ C: 1, A: 2, B: 3 } + graticule polygon + `; + await mermaidAPI.parse(str); + const diagram = await Diagram.fromText(str); + await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + }); + }); }); }); diff --git a/packages/mermaid/src/diagrams/radar/renderer.ts b/packages/mermaid/src/diagrams/radar/renderer.ts index 5a5b47f45..7324a3a44 100644 --- a/packages/mermaid/src/diagrams/radar/renderer.ts +++ b/packages/mermaid/src/diagrams/radar/renderer.ts @@ -118,7 +118,6 @@ const drawAxes = ( } }; -export const renderer: DiagramRenderer = { draw }; function drawCurves( g: SVGGroup, axes: RadarAxis[], @@ -159,12 +158,17 @@ function drawCurves( }); } -function relativeRadius(value: number, minValue: number, maxValue: number, radius: number): number { +export function relativeRadius( + value: number, + minValue: number, + maxValue: number, + radius: number +): number { const clippedValue = Math.min(Math.max(value, minValue), maxValue); return (radius * (clippedValue - minValue)) / (maxValue - minValue); } -function closedRoundCurve(points: { x: number; y: number }[], tension: number): string { +export function closedRoundCurve(points: { x: number; y: number }[], tension: number): string { // Catmull-Rom spline helper function const numPoints = points.length; let d = `M${points[0].x},${points[0].y}`; @@ -224,3 +228,5 @@ function drawLegend( .text(curve.label); }); } + +export const renderer: DiagramRenderer = { draw }; diff --git a/packages/mermaid/src/diagrams/radar/styles.ts b/packages/mermaid/src/diagrams/radar/styles.ts index ddb419d1e..aa389fda3 100644 --- a/packages/mermaid/src/diagrams/radar/styles.ts +++ b/packages/mermaid/src/diagrams/radar/styles.ts @@ -30,12 +30,18 @@ const genIndexStyles = ( return sections; }; -export const styles: DiagramStylesProvider = ({ radar }: { radar?: RadarStyleOptions } = {}) => { +export const buildRadarStyleOptions = (radar?: RadarStyleOptions) => { const defaultThemeVariables = getThemeVariables(); const currentConfig = getConfigAPI(); const themeVariables = cleanAndMerge(defaultThemeVariables, currentConfig.themeVariables); const radarOptions: RadarStyleOptions = cleanAndMerge(themeVariables.radar, radar); + + return { themeVariables, radarOptions }; +}; + +export const styles: DiagramStylesProvider = ({ radar }: { radar?: RadarStyleOptions } = {}) => { + const { themeVariables, radarOptions } = buildRadarStyleOptions(radar); return ` .radarTitle { font-size: ${themeVariables.fontSize}; diff --git a/packages/mermaid/src/diagrams/radar/types.ts b/packages/mermaid/src/diagrams/radar/types.ts index 307fbaf04..b4e280af9 100644 --- a/packages/mermaid/src/diagrams/radar/types.ts +++ b/packages/mermaid/src/diagrams/radar/types.ts @@ -28,16 +28,16 @@ export interface RadarDB extends DiagramDBBase { } export interface RadarStyleOptions { - axisColor: string; - axisStrokeWidth: number; - axisLabelFontSize: number; - curveOpacity: number; - curveStrokeWidth: number; - graticuleColor: string; - graticuleOpacity: number; - graticuleStrokeWidth: number; - legendBoxSize: number; - legendFontSize: number; + axisColor?: string; + axisStrokeWidth?: number; + axisLabelFontSize?: number; + curveOpacity?: number; + curveStrokeWidth?: number; + graticuleColor?: string; + graticuleOpacity?: number; + graticuleStrokeWidth?: number; + legendBoxSize?: number; + legendFontSize?: number; } export interface RadarData { diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js index 1fb35bce6..b21052ea6 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js +++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js @@ -1679,7 +1679,6 @@ Alice->Bob: Hello Bob, how are you?`; const diagram = await Diagram.fromText(str); await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); - const { bounds, models } = diagram.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0);