Files
homelable/frontend/src/components/canvas/edges/__tests__/HomelableEdge.lineStyle.test.tsx
T
Pouzor 485d2f2b04 feat: configurable edge line style + width per type and per edge
Edge render (solid/dashed/dotted) and stroke width were hardcoded per
edge type. Expose both as user settings.

- Custom Style modal (Edges): line-style buttons, 1-4x width slider,
  live preview; left-list swatch renders the actual line.
- Per-edge EdgeModal: same controls; line style follows the type preset
  live until overridden.
- Renderer applies line_style/width_mult over BASE_STYLES (width scales
  markers + animation overlays); unset keeps the type default look.
- Persist line_style/width_mult through serializer, canvas save, and the
  edges API (new nullable columns, idempotent migration).

ha-relevant: yes
2026-07-05 14:11:01 +02:00

71 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest'
import { render } from '@testing-library/react'
import { ReactFlowProvider } from '@xyflow/react'
import type { EdgeProps, Edge } from '@xyflow/react'
import { HomelableEdge } from '../index'
import type { EdgeData } from '@/types'
/**
* Per-edge line render: `line_style` overrides the type's dash preset and
* `width_mult` scales the type base stroke width (1×–4×). Both are optional —
* unset leaves the edge type's default look untouched.
*/
function renderEdge(data: Partial<EdgeData> = {}) {
const props = {
id: 'e1',
source: 'a',
target: 'b',
sourceX: 0,
sourceY: 0,
targetX: 100,
targetY: 100,
sourcePosition: 'bottom',
targetPosition: 'top',
data: { type: 'ethernet', ...data } as EdgeData,
selected: false,
} as unknown as EdgeProps<Edge<EdgeData>>
return render(
<ReactFlowProvider>
<svg>
<HomelableEdge {...props} />
</svg>
</ReactFlowProvider>,
)
}
/** The BaseEdge path is the one carrying the interaction width. */
function edgePath(container: HTMLElement): SVGPathElement {
return container.querySelector('path.react-flow__edge-path') as SVGPathElement
?? (container.querySelector('path') as SVGPathElement)
}
describe('HomelableEdge line style + width', () => {
it('scales stroke width by the multiplier (ethernet base 2 × 3 = 6)', () => {
const { container } = renderEdge({ width_mult: 3 })
expect(edgePath(container).style.strokeWidth).toBe('6')
})
it('keeps the base width when no multiplier is set', () => {
const { container } = renderEdge()
expect(edgePath(container).style.strokeWidth).toBe('2')
})
it('applies a dash pattern for a dashed line style', () => {
const { container } = renderEdge({ line_style: 'dashed', width_mult: 2 })
// width 4 → dashed "12 8"
expect(edgePath(container).style.strokeDasharray.replace(/,/g, '')).toBe('12 8')
})
it('uses a round cap for dotted lines', () => {
const { container } = renderEdge({ line_style: 'dotted' })
expect(edgePath(container).style.strokeLinecap).toBe('round')
})
it('clears the preset dash for a solid override', () => {
// wifi defaults to a dashed preset; solid override removes it
const { container } = renderEdge({ type: 'wifi', line_style: 'solid' })
expect(edgePath(container).style.strokeDasharray).toBe('')
})
})