feat: edit container parent in the node modal instead of detail panel

Replaces the detail-panel container selector with the existing "Parent
Container" dropdown in the edit modal (double-click). The selector now
shows for any node that is nested in — or can nest into — a container_mode
node, not just lxc/vm/docker_container, so a dragged-in node can be
re-targeted or detached ("None") from the modal.

- NodeModal: container_mode candidates count as valid parents; selector
  shown for nested nodes; submit/type-change keep valid container parents
- App: pass container_mode in parentCandidates
- revert detail-panel selector + setNodeParent store action (updateNode
  already handles parent attach/detach)

ha-relevant: yes
This commit is contained in:
Pouzor
2026-06-11 17:25:44 +02:00
parent 10b981ad1d
commit f082c295fd
7 changed files with 64 additions and 229 deletions
@@ -353,6 +353,48 @@ describe('NodeModal', () => {
expect(screen.getByText('Parent Container')).toBeDefined()
})
it('renders Parent Container for a plain node when a container-mode candidate exists', () => {
renderModal({
initial: { ...BASE, type: 'server' },
parentCandidates: [{ id: 'px1', label: 'PVE', type: 'proxmox', container_mode: true }],
})
expect(screen.getByText('Parent Container')).toBeDefined()
})
it('still hides Parent Container for a plain node when the candidate is not in container mode', () => {
renderModal({
initial: { ...BASE, type: 'server' },
parentCandidates: [{ id: 'px1', label: 'PVE', type: 'proxmox', container_mode: false }],
})
expect(screen.queryByText('Parent Container')).toBeNull()
})
it('renders Parent Container for an already-nested plain node so it can be detached', () => {
renderModal({
initial: { ...BASE, type: 'server', parent_id: 'px1' },
parentCandidates: [{ id: 'px1', label: 'PVE', type: 'proxmox', container_mode: true }],
})
expect(screen.getByText('Parent Container')).toBeDefined()
})
it('keeps a container-mode parent_id on submit for a plain node', () => {
const { onSubmit } = renderModal({
initial: { ...BASE, type: 'server', parent_id: 'px1' },
parentCandidates: [{ id: 'px1', label: 'PVE', type: 'proxmox', container_mode: true }],
})
fireEvent.click(screen.getByRole('button', { name: 'Add' }))
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).parent_id).toBe('px1')
})
it('drops a parent_id that is not a valid container on submit', () => {
const { onSubmit } = renderModal({
initial: { ...BASE, type: 'server', parent_id: 'px1' },
parentCandidates: [{ id: 'px1', label: 'PVE', type: 'proxmox', container_mode: false }],
})
fireEvent.click(screen.getByRole('button', { name: 'Add' }))
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).parent_id).toBeUndefined()
})
// ── Appearance ────────────────────────────────────────────────────────
it('renders 3 color swatch labels (border, background, icon)', () => {