Merge pull request #246 from Pouzor/release/2.6.1

Release 2.6.1: Z-Wave manual node type + standalone multi-canvas
This commit is contained in:
Rémy
2026-06-30 14:45:53 +02:00
committed by GitHub
5 changed files with 24 additions and 6 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "frontend",
"version": "2.6.0",
"version": "2.6.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "frontend",
"version": "2.6.0",
"version": "2.6.1",
"dependencies": {
"@base-ui/react": "^1.2.0",
"@dagrejs/dagre": "^2.0.4",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
"version": "2.6.0",
"version": "2.6.1",
"type": "module",
"scripts": {
"dev": "vite",
+6 -2
View File
@@ -18,6 +18,7 @@ const NODE_TYPE_GROUPS: { label: string; types: NodeType[] }[] = [
{ label: 'Virtualization', types: ['proxmox', 'vm', 'lxc', 'docker_host', 'docker_container'] },
{ label: 'IoT', types: ['iot', 'camera', 'cpl'] },
{ label: 'Zigbee', types: ['zigbee_coordinator', 'zigbee_router', 'zigbee_enddevice'] },
{ label: 'Z-Wave', types: ['zwave_coordinator', 'zwave_router', 'zwave_enddevice'] },
{ label: 'Personal', types: ['computer', 'laptop', 'mobile'] },
{ label: 'Electrical', types: ['grid', 'ups', 'battery', 'generator', 'solar_panel', 'inverter', 'circuit_breaker', 'contactor', 'electrical_switch', 'socket', 'light', 'meter', 'transformer', 'load'] },
{ label: 'Generic', types: ['generic', 'groupRect'] },
@@ -26,6 +27,9 @@ const NODE_TYPE_GROUPS: { label: string; types: NodeType[] }[] = [
const CHECK_METHODS: CheckMethod[] = ['none', 'ping', 'http', 'https', 'tcp', 'ssh', 'prometheus', 'health']
const CONTAINER_MODE_TYPES: NodeType[] = ['proxmox', 'vm', 'lxc', 'docker_host']
const ZIGBEE_TYPES: NodeType[] = ['zigbee_coordinator', 'zigbee_router', 'zigbee_enddevice']
const ZWAVE_TYPES: NodeType[] = ['zwave_coordinator', 'zwave_router', 'zwave_enddevice']
// Mesh radio devices aren't IP-reachable, so they default to no status check.
const MESH_TYPES: NodeType[] = [...ZIGBEE_TYPES, ...ZWAVE_TYPES]
const CHECK_METHOD_LABELS: Record<CheckMethod, string> = {
none: 'None',
@@ -73,7 +77,7 @@ interface NodeModalProps {
// initial value is enough - no need for a reset effect.
export function NodeModal({ open, onClose, onSubmit, initial, title = 'Add Node', parentCandidates = [], currentNodeId }: NodeModalProps) {
const merged = { ...DEFAULT_DATA, ...initial }
if (ZIGBEE_TYPES.includes((merged.type ?? '') as NodeType)) merged.check_method = 'none'
if (MESH_TYPES.includes((merged.type ?? '') as NodeType)) merged.check_method = 'none'
const [form, setForm] = useState<Partial<NodeData>>(merged)
const [iconSearch, setIconSearch] = useState('')
const [iconPickerOpen, setIconPickerOpen] = useState(false)
@@ -133,7 +137,7 @@ export function NodeModal({ open, onClose, onSubmit, initial, title = 'Add Node'
const t = v as NodeType
setForm((f) => {
const next: Partial<NodeData> = { ...f, type: t }
if (ZIGBEE_TYPES.includes(t)) next.check_method = 'none' as CheckMethod
if (MESH_TYPES.includes(t)) next.check_method = 'none' as CheckMethod
// Drop the parent only if it's no longer a valid target for the
// new type — keep container-mode parents (any node can nest).
const parent = parentCandidates.find((n) => n.id === f.parent_id)
@@ -188,6 +188,20 @@ describe('NodeModal', () => {
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).type).toBe('nas')
})
it('offers Z-Wave node types and submits one', () => {
const { onSubmit } = renderModal({ initial: BASE })
fireEvent.change(selects()[0], { target: { value: 'zwave_enddevice' } })
fireEvent.click(screen.getByRole('button', { name: 'Add' }))
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).type).toBe('zwave_enddevice')
})
it('forces check_method to none when a Z-Wave type is selected', () => {
const { onSubmit } = renderModal({ initial: { ...BASE, check_method: 'ping' } })
fireEvent.change(selects()[0], { target: { value: 'zwave_coordinator' } })
fireEvent.click(screen.getByRole('button', { name: 'Add' }))
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).check_method).toBe('none')
})
// ── Check method ──────────────────────────────────────────────────────
it('pre-fills check_method from initial', () => {