fix(group): preserve children + size on edit, fix status WS proxy

- Skip NodeModal on double-click for 'group'/'groupRect' (use inline rename)
- Exempt 'group' from properties-clears-height rule in updateNode
  (was collapsing group + hiding children with extent: 'parent')
- Enable ws: true on Vite /api proxy so /api/v1/status/ws/status upgrades
  (status bubbles stayed grey because WS handshake failed)
- Add regression tests for group/groupRect height preservation
This commit is contained in:
Pouzor
2026-05-10 19:30:04 +02:00
parent c6965b827b
commit 2b91f31807
4 changed files with 41 additions and 2 deletions
+5
View File
@@ -465,6 +465,11 @@ export default function App() {
}, []) }, [])
const handleNodeDoubleClick = useCallback((node: Node<NodeData>) => { const handleNodeDoubleClick = useCallback((node: Node<NodeData>) => {
// 'group' uses inline rename (pencil button in header). Opening the
// generic NodeModal would clobber the group's height (via the
// properties-clears-height rule in updateNode) and lose its children.
// 'groupRect' has its own onDoubleClick that already routes to GroupRectModal.
if (node.data.type === 'group' || node.data.type === 'groupRect') return
handleEditNode(node.id) handleEditNode(node.id)
}, [handleEditNode]) }, [handleEditNode])
@@ -724,6 +724,36 @@ describe('canvasStore', () => {
expect(stored?.height).toBeUndefined() expect(stored?.height).toBeUndefined()
}) })
it('updateNode preserves height for group type when properties change (children must not vanish)', () => {
const group: Node<NodeData> = {
id: 'g1',
type: 'group',
position: { x: 0, y: 0 },
width: 360,
height: 240,
data: { label: 'Zone', type: 'group', status: 'unknown', services: [] },
}
useCanvasStore.setState({ nodes: [group], edges: [] })
useCanvasStore.getState().updateNode('g1', { properties: [] })
const stored = useCanvasStore.getState().nodes.find((n) => n.id === 'g1')
expect(stored?.height).toBe(240)
expect(stored?.width).toBe(360)
})
it('updateNode preserves height for groupRect when properties change', () => {
const rect: Node<NodeData> = {
id: 'r1',
type: 'groupRect',
position: { x: 0, y: 0 },
width: 360,
height: 240,
data: { label: 'Rect', type: 'groupRect', status: 'unknown', services: [] },
}
useCanvasStore.setState({ nodes: [rect], edges: [] })
useCanvasStore.getState().updateNode('r1', { properties: [] })
expect(useCanvasStore.getState().nodes.find((n) => n.id === 'r1')?.height).toBe(240)
})
// ── bottom_handles edge remapping ────────────────────────────────────────── // ── bottom_handles edge remapping ──────────────────────────────────────────
it('remaps source edges to "bottom" when bottom_handles is reduced', () => { it('remaps source edges to "bottom" when bottom_handles is reduced', () => {
+1 -1
View File
@@ -209,7 +209,7 @@ export const useCanvasStore = create<CanvasState>((set) => ({
if (n.id !== id) return n if (n.id !== id) return n
const updated: Node<NodeData> = { ...n, data: { ...n.data, ...data } } const updated: Node<NodeData> = { ...n, data: { ...n.data, ...data } }
// When properties change, clear stored height so the node auto-sizes to fit new content // When properties change, clear stored height so the node auto-sizes to fit new content
if ('properties' in data && n.data.type !== 'proxmox' && n.data.type !== 'groupRect') { if ('properties' in data && n.data.type !== 'proxmox' && n.data.type !== 'groupRect' && n.data.type !== 'group') {
updated.height = undefined updated.height = undefined
} }
if ('parent_id' in data) { if ('parent_id' in data) {
+5 -1
View File
@@ -28,7 +28,11 @@ export default defineConfig({
}, },
server: { server: {
proxy: { proxy: {
'/api': 'http://localhost:8000', '/api': {
target: 'http://localhost:8000',
changeOrigin: true,
ws: true,
},
'/ws': { '/ws': {
target: 'ws://localhost:8000', target: 'ws://localhost:8000',
ws: true, ws: true,