test(examples): use new api for test examples

This commit is contained in:
moklick
2021-12-22 14:19:57 +01:00
parent ca77d0ef4c
commit 8014fb0f44
16 changed files with 341 additions and 216 deletions
+6 -6
View File
@@ -127,29 +127,29 @@ describe('Basic Flow Rendering', () => {
}); });
it('drags the pane', () => { it('drags the pane', () => {
const styleBeforeDrag = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeDrag = Cypress.$('.react-flow__viewport').css('transform');
// for d3 we have to pass the window to the event // for d3 we have to pass the window to the event
// https://github.com/cypress-io/cypress/issues/3441 // https://github.com/cypress-io/cypress/issues/3441
cy.window().then((win) => { cy.window().then((win) => {
cy.get('.react-flow__renderer') cy.get('.react-flow__pane')
.trigger('mousedown', 'topLeft', { which: 1, view: win }) .trigger('mousedown', 'topLeft', { which: 1, view: win })
.trigger('mousemove', 'bottomLeft') .trigger('mousemove', 'bottomLeft')
.trigger('mouseup', { force: true, view: win }) .trigger('mouseup', { force: true, view: win })
.then(() => { .then(() => {
const styleAfterDrag = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterDrag = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeDrag).to.not.equal(styleAfterDrag); expect(styleBeforeDrag).to.not.equal(styleAfterDrag);
}); });
}); });
}); });
it('zooms the pane', () => { it('zooms the pane', () => {
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeZoom = Cypress.$('.react-flow__viewport').css('transform');
cy.get('.react-flow__renderer') cy.get('.react-flow__pane')
.trigger('wheel', 'topLeft', { deltaY: -200 }) .trigger('wheel', 'topLeft', { deltaY: -200 })
.then(() => { .then(() => {
const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterZoom = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeZoom).to.not.equal(styleAfterZoom); expect(styleBeforeZoom).to.not.equal(styleAfterZoom);
}); });
}); });
+8 -8
View File
@@ -8,30 +8,30 @@ describe('Controls Testing', () => {
}); });
it('zooms in', () => { it('zooms in', () => {
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeZoom = Cypress.$('.react-flow__viewport').css('transform');
cy.get('.react-flow__controls-zoomin') cy.get('.react-flow__controls-zoomin')
.click() .click()
.then(() => { .then(() => {
const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterZoom = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeZoom).to.not.equal(styleAfterZoom); expect(styleBeforeZoom).to.not.equal(styleAfterZoom);
}); });
}); });
it('zooms out', () => { it('zooms out', () => {
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeZoom = Cypress.$('.react-flow__viewport').css('transform');
cy.get('.react-flow__controls-zoomout') cy.get('.react-flow__controls-zoomout')
.click() .click()
.then(() => { .then(() => {
const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterZoom = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeZoom).to.not.equal(styleAfterZoom); expect(styleBeforeZoom).to.not.equal(styleAfterZoom);
}); });
}); });
// view is already fitted so we drag the pane to un-fit it // view is already fitted so we drag the pane to un-fit it
it('drags the pane', () => { it('drags the pane', () => {
const styleBeforeDrag = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeDrag = Cypress.$('.react-flow__viewport').css('transform');
// for d3 we have to pass the window to the event // for d3 we have to pass the window to the event
// https://github.com/cypress-io/cypress/issues/3441 // https://github.com/cypress-io/cypress/issues/3441
@@ -41,19 +41,19 @@ describe('Controls Testing', () => {
.trigger('mousemove', 'bottomLeft') .trigger('mousemove', 'bottomLeft')
.trigger('mouseup', { force: true, view: win }) .trigger('mouseup', { force: true, view: win })
.then(() => { .then(() => {
const styleAfterDrag = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterDrag = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeDrag).to.not.equal(styleAfterDrag); expect(styleBeforeDrag).to.not.equal(styleAfterDrag);
}); });
}); });
}); });
it('fits view', () => { it('fits view', () => {
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeZoom = Cypress.$('.react-flow__viewport').css('transform');
cy.get('.react-flow__controls-fitview') cy.get('.react-flow__controls-fitview')
.click() .click()
.then(() => { .then(() => {
const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterZoom = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeZoom).to.not.equal(styleAfterZoom); expect(styleBeforeZoom).to.not.equal(styleAfterZoom);
}); });
}); });
-10
View File
@@ -20,11 +20,6 @@ describe('Empty Flow Rendering', () => {
cy.get('body').type('{shift}', { release: true }); cy.get('body').type('{shift}', { release: true });
}); });
it('renders an empty mini map', () => {
cy.get('.react-flow__minimap');
cy.get('.react-flow__minimap-node').should('not.exist');
});
it('adds two nodes', () => { it('adds two nodes', () => {
cy.contains('add node').click(); cy.contains('add node').click();
cy.contains('add node').click(); cy.contains('add node').click();
@@ -41,9 +36,4 @@ describe('Empty Flow Rendering', () => {
cy.get('.react-flow__edge').should('have.length', 1); cy.get('.react-flow__edge').should('have.length', 1);
}); });
it('renders mini map with two nodes', () => {
cy.get('.react-flow__minimap');
cy.get('.react-flow__minimap-node').should('have.length', 2);
});
}); });
+9 -9
View File
@@ -27,41 +27,41 @@ describe('Graph Utils Testing', () => {
}); });
it('tests getOutgoers function', () => { it('tests getOutgoers function', () => {
const outgoers = getOutgoers(nodes[0], elements); const outgoers = getOutgoers(nodes[0], nodes, edges);
expect(outgoers.length).to.be.equal(2); expect(outgoers.length).to.be.equal(2);
const noOutgoers = getOutgoers(nodes[2], elements); const noOutgoers = getOutgoers(nodes[2], nodes, edges);
expect(noOutgoers.length).to.be.equal(0); expect(noOutgoers.length).to.be.equal(0);
}); });
it('tests getIncomers function', () => { it('tests getIncomers function', () => {
const incomers = getIncomers(nodes[2], elements); const incomers = getIncomers(nodes[2], nodes, edges);
expect(incomers.length).to.be.equal(2); expect(incomers.length).to.be.equal(2);
const noIncomers = getIncomers(nodes[0], elements); const noIncomers = getIncomers(nodes[0], nodes, edges);
expect(noIncomers.length).to.be.equal(0); expect(noIncomers.length).to.be.equal(0);
}); });
describe('tests addEdge function', () => { describe('tests addEdge function', () => {
it('adds edge', () => { it('adds edge', () => {
const newEdge = { source: '1', target: '4' }; const newEdge = { source: '1', target: '4' };
const nextElements = addEdge(newEdge, elements); const nextEdges = addEdge(newEdge, edges);
expect(nextElements.length).to.be.equal(elements.length + 1); expect(nextEdges.length).to.be.equal(edges.length + 1);
}); });
it('tries to add existing edge', () => { it('tries to add existing edge', () => {
const newEdge = { source: '2', target: '3' }; const newEdge = { source: '2', target: '3' };
const nextElements = addEdge(newEdge, elements); const nextEdges = addEdge(newEdge, edges);
expect(nextElements.length).to.be.equal(elements.length); expect(nextEdges.length).to.be.equal(edges.length);
}); });
it('tries to add invalid edge', () => { it('tries to add invalid edge', () => {
const newEdge = { nosource: '1', notarget: '3' }; const newEdge = { nosource: '1', notarget: '3' };
try { try {
addEdge(newEdge, elements); addEdge(newEdge, edges);
} catch (e) { } catch (e) {
console.log(e.message); console.log(e.message);
+11 -11
View File
@@ -3,6 +3,16 @@ describe('Hidden Flow Rendering', () => {
cy.visit('/hidden'); cy.visit('/hidden');
}); });
it('renders empty flow', () => {
cy.get('.react-flow__node').should('not.exist');
cy.get('.react-flow__edge').should('not.exist');
cy.get('.react-flow__minimap-node').should('not.exist');
});
it('toggles isHidden mode', () => {
cy.get('.react-flow__ishidden').click();
});
it('renders initial flow', () => { it('renders initial flow', () => {
cy.get('.react-flow__renderer'); cy.get('.react-flow__renderer');
cy.get('.react-flow__node').should('have.length', 4); cy.get('.react-flow__node').should('have.length', 4);
@@ -10,7 +20,7 @@ describe('Hidden Flow Rendering', () => {
cy.get('.react-flow__minimap-node').should('have.length', 4); cy.get('.react-flow__minimap-node').should('have.length', 4);
}); });
it('toggles isHidden mode', () => { it('toggles isHidden mode again', () => {
cy.get('.react-flow__ishidden').click(); cy.get('.react-flow__ishidden').click();
}); });
@@ -19,14 +29,4 @@ describe('Hidden Flow Rendering', () => {
cy.get('.react-flow__edge').should('not.exist'); cy.get('.react-flow__edge').should('not.exist');
cy.get('.react-flow__minimap-node').should('not.exist'); cy.get('.react-flow__minimap-node').should('not.exist');
}); });
it('toggles isHidden mode again', () => {
cy.get('.react-flow__ishidden').click();
});
it('renders initial flow', () => {
cy.get('.react-flow__node').should('have.length', 4);
cy.get('.react-flow__edge').should('have.length', 3);
cy.get('.react-flow__minimap-node').should('have.length', 4);
});
}); });
+6 -6
View File
@@ -120,12 +120,12 @@ describe('Interaction Flow Rendering', () => {
}); });
it('zooms by scroll', () => { it('zooms by scroll', () => {
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeZoom = Cypress.$('.react-flow__viewport').css('transform');
cy.get('.react-flow__renderer') cy.get('.react-flow__pane')
.trigger('wheel', 'topLeft', { deltaY: 200 }) .trigger('wheel', 'topLeft', { deltaY: 200 })
.then(() => { .then(() => {
const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterZoom = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeZoom).not.to.equal(styleAfterZoom); expect(styleBeforeZoom).not.to.equal(styleAfterZoom);
}); });
}); });
@@ -136,12 +136,12 @@ describe('Interaction Flow Rendering', () => {
it('zooms by double click', () => { it('zooms by double click', () => {
cy.get('.react-flow__controls-zoomout').click(); cy.get('.react-flow__controls-zoomout').click();
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleBeforeZoom = Cypress.$('.react-flow__viewport').css('transform');
cy.get('.react-flow__renderer') cy.get('.react-flow__pane')
.dblclick() .dblclick()
.then(() => { .then(() => {
const styleAfterZoom = Cypress.$('.react-flow__nodes').css('transform'); const styleAfterZoom = Cypress.$('.react-flow__viewport').css('transform');
expect(styleBeforeZoom).not.to.equal(styleAfterZoom); expect(styleBeforeZoom).not.to.equal(styleAfterZoom);
}); });
}); });
+2 -2
View File
@@ -19,7 +19,7 @@ describe('Minimap Testing', () => {
const viewBoxBeforeZoom = Cypress.$('.react-flow__minimap').attr('viewBox'); const viewBoxBeforeZoom = Cypress.$('.react-flow__minimap').attr('viewBox');
const maskPathBeforeZoom = Cypress.$('.react-flow__minimap-mask').attr('d'); const maskPathBeforeZoom = Cypress.$('.react-flow__minimap-mask').attr('d');
cy.get('.react-flow__renderer') cy.get('.react-flow__pane')
.trigger('wheel', 'topLeft', { deltaY: -200 }) .trigger('wheel', 'topLeft', { deltaY: -200 })
.then(() => { .then(() => {
const viewBoxAfterZoom = Cypress.$('.react-flow__minimap').attr('viewBox'); const viewBoxAfterZoom = Cypress.$('.react-flow__minimap').attr('viewBox');
@@ -53,7 +53,7 @@ describe('Minimap Testing', () => {
// for d3 we have to pass the window to the event // for d3 we have to pass the window to the event
// https://github.com/cypress-io/cypress/issues/3441 // https://github.com/cypress-io/cypress/issues/3441
cy.window().then((win) => { cy.window().then((win) => {
cy.get('.react-flow__renderer') cy.get('.react-flow__pane')
.trigger('mousedown', 'topLeft', { which: 1, view: win }) .trigger('mousedown', 'topLeft', { which: 1, view: win })
.trigger('mousemove', 'bottomLeft') .trigger('mousemove', 'bottomLeft')
.trigger('mouseup', { force: true, view: win }) .trigger('mouseup', { force: true, view: win })
+17 -115
View File
@@ -1,130 +1,49 @@
import { useState, MouseEvent, useCallback } from 'react'; import { useState, MouseEvent } from 'react';
import ReactFlow, { import ReactFlow, {
addEdge, addEdge,
Background, Background,
MiniMap, BackgroundVariant,
Controls, FlowElement,
Node, Node,
Edge, Edge,
ReactFlowInstance,
Connection, Connection,
MarkerType,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
ReactFlowInstance,
} from 'react-flow-renderer'; } from 'react-flow-renderer';
import DebugNode from './DebugNode';
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); const onNodeClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge);
const initialNodes: Node[] = [ const initialNodes: Node[] = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' }, { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
{
id: '4',
data: { label: 'Node 4' },
position: { x: 100, y: 200 },
className: 'light',
style: { backgroundColor: 'rgba(255,50, 50, 0.5)', width: 500, height: 300 },
},
{
id: '4a',
data: { label: 'Node 4a' },
position: { x: 15, y: 15 },
className: 'light',
parentNode: '4',
extent: 'parent',
},
{
id: '4b',
data: { label: 'Node 4b' },
position: { x: 150, y: 50 },
className: 'light',
style: { backgroundColor: 'rgba(50, 50, 255, 0.5)', height: 200, width: 300 },
parentNode: '4',
},
{
id: '4b1',
data: { label: 'Node 4b1' },
position: { x: 20, y: 20 },
className: 'light',
parentNode: '4b',
},
{
id: '4b2',
data: { label: 'Node 4b2' },
position: { x: 100, y: 100 },
className: 'light',
parentNode: '4b',
},
{
id: '5',
data: { label: 'Node 5' },
position: { x: 650, y: 250 },
className: 'light',
style: { backgroundColor: 'rgba(20 ,200, 255, 1.5)', width: 400, height: 150 },
zIndex: 1000,
},
{
id: '5a',
data: { label: 'Node 5a' },
position: { x: 25, y: 50 },
className: 'light',
parentNode: '5',
},
{
id: '5b',
data: { label: 'Node 5b' },
position: { x: 225, y: 50 },
className: 'light',
parentNode: '5',
},
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' }, { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' }, { id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' },
]; ];
const initialEdges: Edge[] = [ const initialEdges: Edge[] = [
{ { id: 'e1-2', source: '1', target: '2', animated: true },
id: 'e1-2',
source: '1',
target: '2',
markerEnd: { type: MarkerType.Arrow, strokeWidth: 2, width: 15, height: 15, color: '#f00' },
},
{ id: 'e1-3', source: '1', target: '3' }, { id: 'e1-3', source: '1', target: '3' },
{ id: 'e3-4', source: '3', target: '4', zIndex: 100 },
{ id: 'e3-4b', source: '3', target: '4b' },
{ id: 'e4a-4b1', source: '4a', target: '4b1' },
{ id: 'e4a-4b2', source: '4a', target: '4b2', zIndex: 100 },
{ id: 'e4b1-4b2', source: '4b1', target: '4b2' },
{ id: '3-5', source: '3', target: '5' },
]; ];
const nodeTypes = {
default: DebugNode,
};
const BasicFlow = () => { const BasicFlow = () => {
const [rfInstance, setRfInstance] = useState<ReactFlowInstance | null>(null); const [rfInstance, setRfInstance] = useState<ReactFlowInstance | null>(null);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: Edge | Connection) => setEdges((els) => addEdge(params, els));
const onConnect = useCallback((connection: Connection) => { const onPaneReady = (reactFlowInstance: ReactFlowInstance) => setRfInstance(reactFlowInstance);
setEdges((eds) => {
return addEdge(connection, eds);
});
}, []);
const onPaneReady = useCallback((reactFlowInstance: ReactFlowInstance) => setRfInstance(reactFlowInstance), []);
const updatePos = () => { const updatePos = () => {
setNodes((nds) => { setNodes((nds) => {
return nds.map((n) => { return nds.map((node) => {
n.position = { node.position = {
x: Math.random() * 400, x: Math.random() * 400,
y: Math.random() * 400, y: Math.random() * 400,
}; };
return n; return node;
}); });
}); });
}; };
@@ -134,18 +53,10 @@ const BasicFlow = () => {
const toggleClassnames = () => { const toggleClassnames = () => {
setNodes((nds) => { setNodes((nds) => {
return nds.map((n) => { return nds.map((node) => {
n.className = n.className === 'light' ? 'dark' : 'light'; node.className = node.className === 'light' ? 'dark' : 'light';
return n;
});
});
};
const toggleChildNodes = () => { return node;
setNodes((nds) => {
return nds.map((n) => {
n.hidden = !!n.parentNode && !n.hidden;
return n;
}); });
}); });
}; };
@@ -154,24 +65,18 @@ const BasicFlow = () => {
<ReactFlow <ReactFlow
nodes={nodes} nodes={nodes}
edges={edges} edges={edges}
onPaneReady={onPaneReady}
onNodesChange={onNodesChange} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange} onEdgesChange={onEdgesChange}
onPaneReady={onPaneReady}
onNodeClick={onNodeClick} onNodeClick={onNodeClick}
onEdgeClick={onEdgeClick}
onConnect={onConnect} onConnect={onConnect}
onNodeDragStop={onNodeDragStop} onNodeDragStop={onNodeDragStop}
className="react-flow-basic-example" className="react-flow-basic-example"
defaultZoom={1.5} defaultZoom={1.5}
minZoom={0.2} minZoom={0.2}
maxZoom={4} maxZoom={4}
onlyRenderVisibleElements={false}
nodeTypes={nodeTypes}
fitViewOnInit
> >
<MiniMap /> <Background variant={BackgroundVariant.Lines} />
<Controls />
<Background />
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}> <div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
<button onClick={resetTransform} style={{ marginRight: 5 }}> <button onClick={resetTransform} style={{ marginRight: 5 }}>
@@ -183,9 +88,6 @@ const BasicFlow = () => {
<button onClick={toggleClassnames} style={{ marginRight: 5 }}> <button onClick={toggleClassnames} style={{ marginRight: 5 }}>
toggle classnames toggle classnames
</button> </button>
<button style={{ marginRight: 5 }} onClick={toggleChildNodes}>
toggleChildNodes
</button>
<button onClick={logToObject}>toObject</button> <button onClick={logToObject}>toObject</button>
</div> </div>
</ReactFlow> </ReactFlow>
+29
View File
@@ -0,0 +1,29 @@
import ReactFlow, { Node, Edge, useNodesState, useEdgesState } from 'react-flow-renderer';
import DragHandleNode from './DragHandleNode';
const nodeTypes = {
dragHandleNode: DragHandleNode,
};
const initialNodes: Node[] = [
{
id: '2',
type: 'dragHandleNode',
dragHandle: '.custom-drag-handle',
style: { border: '1px solid #ddd', padding: '20px 40px' },
position: { x: 200, y: 200 },
data: null,
},
];
const initialEdges: Edge[] = [];
const DragHandleFlow = () => {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges] = useEdgesState(initialEdges);
return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} edges={edges} nodeTypes={nodeTypes} />;
};
export default DragHandleFlow;
@@ -1,52 +1,51 @@
import React, { useState, MouseEvent, CSSProperties } from 'react'; import { MouseEvent, CSSProperties } from 'react';
import ReactFlow, { import ReactFlow, {
removeElements,
addEdge, addEdge,
MiniMap,
Controls, Controls,
Background, Background,
OnLoadParams,
Elements,
ElementId,
Node, Node,
FlowElement,
BackgroundVariant, BackgroundVariant,
Connection, Connection,
Edge, Edge,
useNodesState,
useEdgesState,
ReactFlowInstance,
} from 'react-flow-renderer'; } from 'react-flow-renderer';
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance); const onPaneReady = (reactFlowInstance: ReactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const buttonStyle: CSSProperties = { position: 'absolute', left: 10, top: 10, zIndex: 4 }; const buttonStyle: CSSProperties = { position: 'absolute', left: 10, top: 10, zIndex: 4 };
const EmptyFlow = () => { const EmptyFlow = () => {
const [elements, setElements] = useState<Elements>([]); const [nodes, setNodes, onNodesChange] = useNodesState([]);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
const onConnect = (params: Connection | Edge) => setEdges((els) => addEdge(params, els));
const addRandomNode = () => { const addRandomNode = () => {
const nodeId: ElementId = (elements.length + 1).toString(); const nodeId = (nodes.length + 1).toString();
const newNode: Node = { const newNode: Node = {
id: nodeId, id: nodeId,
data: { label: `Node: ${nodeId}` }, data: { label: `Node: ${nodeId}` },
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight }, position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight },
}; };
setElements((els) => els.concat(newNode)); setNodes((nds) => nds.concat(newNode));
}; };
return ( return (
<ReactFlow <ReactFlow
elements={elements} nodes={nodes}
onLoad={onLoad} edges={edges}
onElementClick={onElementClick} onPaneReady={onPaneReady}
onElementsRemove={onElementsRemove} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={onNodeClick}
onConnect={(p) => onConnect(p)} onConnect={(p) => onConnect(p)}
onNodeDragStop={onNodeDragStop} onNodeDragStop={onNodeDragStop}
onlyRenderVisibleElements={false} onlyRenderVisibleElements={false}
> >
<MiniMap />
<Controls /> <Controls />
<Background variant={BackgroundVariant.Lines} /> <Background variant={BackgroundVariant.Lines} />
@@ -1,37 +1,42 @@
import React, { useState, MouseEvent, WheelEvent } from 'react'; import { useState, MouseEvent, WheelEvent } from 'react';
import ReactFlow, { import ReactFlow, {
addEdge, addEdge,
MiniMap, MiniMap,
Controls, Controls,
Elements,
Node, Node,
FlowElement,
Connection, Connection,
Edge, Edge,
PanOnScrollMode, PanOnScrollMode,
FlowTransform, FlowTransform,
useNodesState,
useEdgesState,
} from 'react-flow-renderer'; } from 'react-flow-renderer';
const initialElements: Elements = [ const initialNodes: Node[] = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }, { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } }, { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } }, { id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } }, { id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
];
const initialEdges: Edge[] = [
{ id: 'e1-2', source: '1', target: '2', animated: true }, { id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e1-3', source: '1', target: '3' }, { id: 'e1-3', source: '1', target: '3' },
]; ];
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node); const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge);
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event); const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event);
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event); const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event); const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event);
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform); const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform);
const InteractionFlow = () => { const InteractionFlow = () => {
const [elements, setElements] = useState<Elements>(initialElements); const [nodes, , onNodesChange] = useNodesState(initialNodes);
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: Connection | Edge) => setEdges((els) => addEdge(params, els));
const [isSelectable, setIsSelectable] = useState<boolean>(false); const [isSelectable, setIsSelectable] = useState<boolean>(false);
const [isDraggable, setIsDraggable] = useState<boolean>(false); const [isDraggable, setIsDraggable] = useState<boolean>(false);
@@ -46,9 +51,14 @@ const InteractionFlow = () => {
const [captureZoomScroll, setCaptureZoomScroll] = useState<boolean>(false); const [captureZoomScroll, setCaptureZoomScroll] = useState<boolean>(false);
const [captureElementClick, setCaptureElementClick] = useState<boolean>(false); const [captureElementClick, setCaptureElementClick] = useState<boolean>(false);
console.log(11, captureElementClick);
return ( return (
<ReactFlow <ReactFlow
elements={elements} nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
elementsSelectable={isSelectable} elementsSelectable={isSelectable}
nodesConnectable={isConnectable} nodesConnectable={isConnectable}
nodesDraggable={isDraggable} nodesDraggable={isDraggable}
@@ -58,7 +68,8 @@ const InteractionFlow = () => {
panOnScrollMode={panOnScrollMode} panOnScrollMode={panOnScrollMode}
zoomOnDoubleClick={zoomOnDoubleClick} zoomOnDoubleClick={zoomOnDoubleClick}
onConnect={onConnect} onConnect={onConnect}
onElementClick={captureElementClick ? onElementClick : undefined} onNodeClick={captureElementClick ? onNodeClick : undefined}
onEdgeClick={captureElementClick ? onEdgeClick : undefined}
onNodeDragStart={onNodeDragStart} onNodeDragStart={onNodeDragStart}
onNodeDragStop={onNodeDragStop} onNodeDragStop={onNodeDragStop}
paneMoveable={paneMoveable} paneMoveable={paneMoveable}
+195
View File
@@ -0,0 +1,195 @@
import { useState, MouseEvent, useCallback } from 'react';
import ReactFlow, {
addEdge,
Background,
MiniMap,
Controls,
Node,
Edge,
ReactFlowInstance,
Connection,
MarkerType,
useNodesState,
useEdgesState,
} from 'react-flow-renderer';
import DebugNode from './DebugNode';
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge);
const initialNodes: Node[] = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
{
id: '4',
data: { label: 'Node 4' },
position: { x: 100, y: 200 },
className: 'light',
style: { backgroundColor: 'rgba(255,50, 50, 0.5)', width: 500, height: 300 },
},
{
id: '4a',
data: { label: 'Node 4a' },
position: { x: 15, y: 15 },
className: 'light',
parentNode: '4',
extent: 'parent',
},
{
id: '4b',
data: { label: 'Node 4b' },
position: { x: 150, y: 50 },
className: 'light',
style: { backgroundColor: 'rgba(50, 50, 255, 0.5)', height: 200, width: 300 },
parentNode: '4',
},
{
id: '4b1',
data: { label: 'Node 4b1' },
position: { x: 20, y: 20 },
className: 'light',
parentNode: '4b',
},
{
id: '4b2',
data: { label: 'Node 4b2' },
position: { x: 100, y: 100 },
className: 'light',
parentNode: '4b',
},
{
id: '5',
data: { label: 'Node 5' },
position: { x: 650, y: 250 },
className: 'light',
style: { backgroundColor: 'rgba(20 ,200, 255, 1.5)', width: 400, height: 150 },
zIndex: 1000,
},
{
id: '5a',
data: { label: 'Node 5a' },
position: { x: 25, y: 50 },
className: 'light',
parentNode: '5',
},
{
id: '5b',
data: { label: 'Node 5b' },
position: { x: 225, y: 50 },
className: 'light',
parentNode: '5',
},
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
];
const initialEdges: Edge[] = [
{
id: 'e1-2',
source: '1',
target: '2',
markerEnd: { type: MarkerType.Arrow, strokeWidth: 2, width: 15, height: 15, color: '#f00' },
},
{ id: 'e1-3', source: '1', target: '3' },
{ id: 'e3-4', source: '3', target: '4', zIndex: 100 },
{ id: 'e3-4b', source: '3', target: '4b' },
{ id: 'e4a-4b1', source: '4a', target: '4b1' },
{ id: 'e4a-4b2', source: '4a', target: '4b2', zIndex: 100 },
{ id: 'e4b1-4b2', source: '4b1', target: '4b2' },
{ id: '3-5', source: '3', target: '5' },
];
const nodeTypes = {
default: DebugNode,
};
const BasicFlow = () => {
const [rfInstance, setRfInstance] = useState<ReactFlowInstance | null>(null);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback((connection: Connection) => {
setEdges((eds) => {
return addEdge(connection, eds);
});
}, []);
const onPaneReady = useCallback((reactFlowInstance: ReactFlowInstance) => setRfInstance(reactFlowInstance), []);
const updatePos = () => {
setNodes((nds) => {
return nds.map((n) => {
n.position = {
x: Math.random() * 400,
y: Math.random() * 400,
};
return n;
});
});
};
const logToObject = () => console.log(rfInstance?.toObject());
const resetTransform = () => rfInstance?.setTransform({ x: 0, y: 0, zoom: 1 });
const toggleClassnames = () => {
setNodes((nds) => {
return nds.map((n) => {
n.className = n.className === 'light' ? 'dark' : 'light';
return n;
});
});
};
const toggleChildNodes = () => {
setNodes((nds) => {
return nds.map((n) => {
n.hidden = !!n.parentNode && !n.hidden;
return n;
});
});
};
return (
<ReactFlow
nodes={nodes}
edges={edges}
onPaneReady={onPaneReady}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={onNodeClick}
onEdgeClick={onEdgeClick}
onConnect={onConnect}
onNodeDragStop={onNodeDragStop}
className="react-flow-basic-example"
defaultZoom={1.5}
minZoom={0.2}
maxZoom={4}
onlyRenderVisibleElements={false}
nodeTypes={nodeTypes}
fitViewOnInit
>
<MiniMap />
<Controls />
<Background />
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
<button onClick={resetTransform} style={{ marginRight: 5 }}>
reset transform
</button>
<button onClick={updatePos} style={{ marginRight: 5 }}>
change pos
</button>
<button onClick={toggleClassnames} style={{ marginRight: 5 }}>
toggle classnames
</button>
<button style={{ marginRight: 5 }} onClick={toggleChildNodes}>
toggleChildNodes
</button>
<button onClick={logToObject}>toObject</button>
</div>
</ReactFlow>
);
};
export default BasicFlow;
+20
View File
@@ -13,6 +13,10 @@ import NestedNodes from './NestedNodes';
import Hidden from './Hidden'; import Hidden from './Hidden';
import UpdatableEdge from './UpdatableEdge'; import UpdatableEdge from './UpdatableEdge';
import TouchDevice from './TouchDevice'; import TouchDevice from './TouchDevice';
import Subflow from './Subflow';
import Interaction from './Interaction';
import Empty from './Empty';
import DragHandle from './DragHandle';
import './index.css'; import './index.css';
@@ -61,6 +65,22 @@ const routes = [
path: '/touch-device', path: '/touch-device',
component: TouchDevice, component: TouchDevice,
}, },
{
path: '/subflow',
component: Subflow,
},
{
path: '/interaction',
component: Interaction,
},
{
path: '/empty',
component: Empty,
},
{
path: '/draghandle',
component: DragHandle,
},
]; ];
const Header = withRouter(({ history, location }) => { const Header = withRouter(({ history, location }) => {
-21
View File
@@ -1,21 +0,0 @@
import ReactFlow from 'react-flow-renderer';
import DragHandleNode from './DragHandleNode';
const nodeTypes = {
dragHandleNode: DragHandleNode,
};
const elements = [
{
id: '2',
type: 'dragHandleNode',
dragHandle: '.custom-drag-handle',
style: { border: '1px solid #ddd', padding: '20px 40px' },
position: { x: 200, y: 200 },
},
];
const DragHandleFlow = () => <ReactFlow elements={elements} nodeTypes={nodeTypes} />;
export default DragHandleFlow;