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
This commit is contained in:
@@ -146,6 +146,19 @@ describe('CustomStyleModal', () => {
|
||||
expect(applyTypeEdgeStyle.mock.calls[0][1].arrowStart).toBe('none')
|
||||
})
|
||||
|
||||
it('picking a line style + width feeds lineStyle/widthMult to applyTypeEdgeStyle', () => {
|
||||
const applyTypeEdgeStyle = vi.fn()
|
||||
useCanvasStore.setState({ applyTypeEdgeStyle })
|
||||
render(<CustomStyleModal open onClose={vi.fn()} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Edges' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: /Ethernet/ }))
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Dotted' }))
|
||||
fireEvent.change(screen.getByRole('slider', { name: 'Line width multiplier' }), { target: { value: '3' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: /Apply to existing Ethernet/ }))
|
||||
expect(applyTypeEdgeStyle.mock.calls[0][1].lineStyle).toBe('dotted')
|
||||
expect(applyTypeEdgeStyle.mock.calls[0][1].widthMult).toBe(3)
|
||||
})
|
||||
|
||||
it('editing path style updates the edge draft', () => {
|
||||
render(<CustomStyleModal open onClose={vi.fn()} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Edges' }))
|
||||
|
||||
@@ -125,6 +125,41 @@ describe('EdgeModal', () => {
|
||||
expect(onSubmit.mock.calls[0][0].path_style).toBe('smooth')
|
||||
})
|
||||
|
||||
// ── Line style + width ────────────────────────────────────────────────────
|
||||
|
||||
it('defaults line style to the edge type preset (ethernet → solid)', () => {
|
||||
const onSubmit = vi.fn()
|
||||
render(<EdgeModal open onClose={vi.fn()} onSubmit={onSubmit} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Connect' }))
|
||||
expect(onSubmit.mock.calls[0][0].line_style).toBe('solid')
|
||||
expect(onSubmit.mock.calls[0][0].width_mult).toBe(1)
|
||||
})
|
||||
|
||||
it('follows the type default (wifi → dashed) until overridden', () => {
|
||||
const onSubmit = vi.fn()
|
||||
render(<EdgeModal open onClose={vi.fn()} onSubmit={onSubmit} initial={{ type: 'wifi' }} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Connect' }))
|
||||
expect(onSubmit.mock.calls[0][0].line_style).toBe('dashed')
|
||||
})
|
||||
|
||||
it('picking a line style + width sends line_style/width_mult', () => {
|
||||
const onSubmit = vi.fn()
|
||||
render(<EdgeModal open onClose={vi.fn()} onSubmit={onSubmit} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Line style dotted' }))
|
||||
fireEvent.change(screen.getByRole('slider', { name: 'Line width multiplier' }), { target: { value: '4' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Connect' }))
|
||||
expect(onSubmit.mock.calls[0][0].line_style).toBe('dotted')
|
||||
expect(onSubmit.mock.calls[0][0].width_mult).toBe(4)
|
||||
})
|
||||
|
||||
it('pre-fills line style + width from initial prop', () => {
|
||||
const onSubmit = vi.fn()
|
||||
render(<EdgeModal open onClose={vi.fn()} onSubmit={onSubmit} initial={{ line_style: 'dashed', width_mult: 3 }} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Connect' }))
|
||||
expect(onSubmit.mock.calls[0][0].line_style).toBe('dashed')
|
||||
expect(onSubmit.mock.calls[0][0].width_mult).toBe(3)
|
||||
})
|
||||
|
||||
// ── Animation select ──────────────────────────────────────────────────────
|
||||
|
||||
it('animation defaults to None — animated omitted from payload', () => {
|
||||
|
||||
Reference in New Issue
Block a user