Merge pull request #287 from Pouzor/fix/container-host-edit-height-reset

fix(canvas): keep container host height when editing an unrelated field
This commit is contained in:
Pouzor - Rémy Jardient
2026-07-16 17:02:18 +02:00
committed by GitHub
3 changed files with 58 additions and 3 deletions
@@ -118,4 +118,53 @@ describe('canvasStore — sizing & z-order', () => {
useCanvasStore.getState().updateNode('r1', { properties: [] }) useCanvasStore.getState().updateNode('r1', { properties: [] })
expect(useCanvasStore.getState().nodes.find((n) => n.id === 'r1')?.height).toBe(240) expect(useCanvasStore.getState().nodes.find((n) => n.id === 'r1')?.height).toBe(240)
}) })
// Regression (#278): editing any field on a container-mode vm/lxc/docker_host host used to
// reset its manually-set height to undefined, snapping it back to auto-fit size and
// scrambling nested children. Only proxmox was excluded. Keep the manual height for every
// container-mode host type.
it.each(['vm', 'lxc', 'docker_host'] as const)(
'updateNode preserves manual height for a container-mode %s when properties change',
(type) => {
const host: Node<NodeData> = {
id: 'h1',
type,
position: { x: 0, y: 0 },
width: 400,
height: 300,
data: { label: type, type, status: 'unknown', services: [], container_mode: true },
}
useCanvasStore.setState({ nodes: [host], edges: [] })
useCanvasStore.getState().updateNode('h1', { properties: [], label: 'renamed' })
expect(useCanvasStore.getState().nodes.find((n) => n.id === 'h1')?.height).toBe(300)
}
)
it('updateNode still resets height for a vm NOT in container mode when properties change', () => {
const leaf: Node<NodeData> = {
id: 'v1',
type: 'vm',
position: { x: 0, y: 0 },
width: 200,
height: 120,
data: { label: 'vm', type: 'vm', status: 'unknown', services: [], container_mode: false },
}
useCanvasStore.setState({ nodes: [leaf], edges: [] })
useCanvasStore.getState().updateNode('v1', { properties: [] })
expect(useCanvasStore.getState().nodes.find((n) => n.id === 'v1')?.height).toBeUndefined()
})
it('updateNode preserves height for a proxmox host when properties change', () => {
const px: Node<NodeData> = {
id: 'px1',
type: 'proxmox',
position: { x: 0, y: 0 },
width: 500,
height: 350,
data: { label: 'px', type: 'proxmox', status: 'unknown', services: [], container_mode: true },
}
useCanvasStore.setState({ nodes: [px], edges: [] })
useCanvasStore.getState().updateNode('px1', { properties: [] })
expect(useCanvasStore.getState().nodes.find((n) => n.id === 'px1')?.height).toBe(350)
})
}) })
+8 -2
View File
@@ -13,6 +13,7 @@ import { generateUUID } from '@/utils/uuid'
import { normalizeHandle, removedHandleIds, handleCountField, sideDefault, handleId, SIDES } from '@/utils/handleUtils' import { normalizeHandle, removedHandleIds, handleCountField, sideDefault, handleId, SIDES } from '@/utils/handleUtils'
import { applyOpacity } from '@/utils/colorUtils' import { applyOpacity } from '@/utils/colorUtils'
import { readHideIp, writeHideIp } from '@/utils/ipDisplay' import { readHideIp, writeHideIp } from '@/utils/ipDisplay'
import { CONTAINER_MODE_TYPES } from '@/utils/virtualEdgeParent'
type HistoryEntry = { nodes: Node<NodeData>[]; edges: Edge<EdgeData>[] } type HistoryEntry = { nodes: Node<NodeData>[]; edges: Edge<EdgeData>[] }
type Clipboard = { nodes: Node<NodeData>[]; edges: Edge<EdgeData>[] } type Clipboard = { nodes: Node<NodeData>[]; edges: Edge<EdgeData>[] }
@@ -325,8 +326,13 @@ export const useCanvasStore = create<CanvasState>((set) => ({
let nodes = state.nodes.map((n) => { let nodes = state.nodes.map((n) => {
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' && n.data.type !== 'group') { // A container-mode host (vm/lxc/docker_host) keeps a manually-set height: resetting it
// snaps the container back to auto-fit size and scrambles its nested children (#278).
// proxmox is always excluded (legacy behavior); the other container types are excluded
// only while actually in container mode.
const isContainerHost = CONTAINER_MODE_TYPES.has(n.data.type) && !!n.data.container_mode
if ('properties' in data && n.data.type !== 'proxmox' && n.data.type !== 'groupRect' && n.data.type !== 'group' && !isContainerHost) {
updated.height = undefined updated.height = undefined
} }
if ('parent_id' in data) { if ('parent_id' in data) {
+1 -1
View File
@@ -2,7 +2,7 @@ import type { NodeData } from '@/types'
export type NodeType = NodeData['type'] export type NodeType = NodeData['type']
const CONTAINER_MODE_TYPES = new Set<NodeType>(['proxmox', 'vm', 'lxc', 'docker_host']) export const CONTAINER_MODE_TYPES = new Set<NodeType>(['proxmox', 'vm', 'lxc', 'docker_host'])
const DOCKER_CONTAINER_PARENT_TYPES = new Set<NodeType>(['docker_host', 'lxc', 'vm', 'proxmox']) const DOCKER_CONTAINER_PARENT_TYPES = new Set<NodeType>(['docker_host', 'lxc', 'vm', 'proxmox'])
export interface VirtualEdgeEndpoint { export interface VirtualEdgeEndpoint {