diff --git a/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx b/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx index e02f81a..cf20e0a 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx @@ -1,9 +1,9 @@ import { useDraggable } from '@neodrag/react'; import { useReactFlow, type XYPosition } from '@xyflow/react'; import { type ReactNode, useCallback, useRef, useState } from 'react'; -import useFlowStore from '../VisProgStores'; import styles from '../../VisProg.module.css'; import { NodeDefaults, type NodeTypes } from '../NodeRegistry' +import addNode from '../utils/AddNode'; /** * DraggableNodeProps dictates the type properties of a DraggableNode @@ -47,41 +47,6 @@ function DraggableNode({ className, children, nodeType, onDrop }: DraggableNodeP ); } -/** - * addNode — adds a new node to the flow using the unified class-based system. - * Keeps numbering logic for phase/norm nodes. - */ -function addNode(nodeType: keyof typeof NodeTypes, position: XYPosition) { - const { nodes, setNodes } = useFlowStore.getState(); - - // Find out if there's any default data about our ndoe - const defaultData = NodeDefaults[nodeType] ?? {} - - // Currently, we find out what the Id is by checking the last node and adding one - const sameTypeNodes = nodes.filter((node) => node.type === nodeType); - const nextNumber = - sameTypeNodes.length > 0 - ? (() => { - const lastNode = sameTypeNodes[sameTypeNodes.length - 1]; - const parts = lastNode.id.split('-'); - const lastNum = Number(parts[1]); - return Number.isNaN(lastNum) ? sameTypeNodes.length + 1 : lastNum + 1; - })() - : 1; - const id = `${nodeType}-${nextNumber}`; - - // Create new node - const newNode = { - id: id, - type: nodeType, - position, - // Deep copy using JSON because thats how things work: - // Ref: https://developer.mozilla.org/en-US/docs/Glossary/Deep_copy - data: structuredClone(defaultData) - } - setNodes([...nodes, newNode]); -} - /** * DndToolbar defines how the drag and drop toolbar component works * and includes the default onDrop behavior. diff --git a/src/pages/VisProgPage/visualProgrammingUI/utils/AddNode.ts b/src/pages/VisProgPage/visualProgrammingUI/utils/AddNode.ts new file mode 100644 index 0000000..b73d46b --- /dev/null +++ b/src/pages/VisProgPage/visualProgrammingUI/utils/AddNode.ts @@ -0,0 +1,39 @@ +import type { XYPosition } from "@xyflow/react"; +import { NodeDefaults, type NodeTypes } from "../NodeRegistry"; +import useFlowStore from "../VisProgStores"; + + +/** + * addNode — adds a new node to the flow using the unified class-based system. + * Keeps numbering logic for phase/norm nodes. + */ +export default function addNode(nodeType: keyof typeof NodeTypes, position: XYPosition) { + const { nodes, setNodes } = useFlowStore.getState(); + + // Find out if there's any default data about our ndoe + const defaultData = NodeDefaults[nodeType] ?? {} + + // Currently, we find out what the Id is by checking the last node and adding one + const sameTypeNodes = nodes.filter((node) => node.type === nodeType); + const nextNumber = + sameTypeNodes.length > 0 + ? (() => { + const lastNode = sameTypeNodes[sameTypeNodes.length - 1]; + const parts = lastNode.id.split('-'); + const lastNum = Number(parts[1]); + return Number.isNaN(lastNum) ? sameTypeNodes.length + 1 : lastNum + 1; + })() + : 1; + const id = `${nodeType}-${nextNumber}`; + + // Create new node + const newNode = { + id: id, + type: nodeType, + position, + // Deep copy using JSON because thats how things work: + // Ref: https://developer.mozilla.org/en-US/docs/Glossary/Deep_copy + data: JSON.parse(JSON.stringify(defaultData)) + } + setNodes([...nodes, newNode]); +} \ No newline at end of file diff --git a/test/pages/visProgPage/visualProgrammingUI/nodes/PhaseNode.test.tsx b/test/pages/visProgPage/visualProgrammingUI/nodes/PhaseNode.test.tsx new file mode 100644 index 0000000..b37c23a --- /dev/null +++ b/test/pages/visProgPage/visualProgrammingUI/nodes/PhaseNode.test.tsx @@ -0,0 +1,22 @@ +import { resetFlowStore } from "../../../../test-utils/test-utils"; +import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores'; +import addNode from "../../../../../src/pages/VisProgPage/visualProgrammingUI/utils/AddNode"; + + +describe('PhaseNode', () => { + beforeEach(() => resetFlowStore()); + + it('each created phase gets its own children array (store-level)', () => { + addNode("phase", {x:10,y:10}) + addNode("phase", {x:20,y:20}) + + const nodes = useFlowStore.getState().nodes; + const p1 = nodes.find((x) => x.id === 'phase-1')!; + const p2 = nodes.find((x) => x.id === 'phase-2')!; + + // not the same reference + expect(p1.data.children).not.toBe(p2.data.children); + // but same initial value + expect(p1.data.children).toEqual(p2.data.children); + }); +}); \ No newline at end of file diff --git a/test/pages/visProgPage/visualProgrammingUI/nodes/UniversalNodes.test.tsx b/test/pages/visProgPage/visualProgrammingUI/nodes/UniversalNodes.test.tsx index 1119860..182ff53 100644 --- a/test/pages/visProgPage/visualProgrammingUI/nodes/UniversalNodes.test.tsx +++ b/test/pages/visProgPage/visualProgrammingUI/nodes/UniversalNodes.test.tsx @@ -15,7 +15,7 @@ describe('NormNode', () => { }); function createNode(id: string, type: string, position: XYPosition, data: Record, deletable?: boolean) { - const defaultData = NodeDefaults[type as keyof typeof NodeDefaults] + const defaultData = JSON.parse(JSON.stringify(NodeDefaults[type as keyof typeof NodeDefaults])) const newData = { id: id, type: type,