chore(svelte-examples): pull out of lib package (#3363)
* chore(svelte-examples): pull out of lib package * chore(svelte-examples): cleanup * chore(examples): add readme files
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
import { ReactFlow, EdgeProps } from '@xyflow/react';
|
||||
|
||||
import ControlledFlow from '../../support/ControlledFlow';
|
||||
import * as simpleflow from '../../fixtures/simpleflow';
|
||||
|
||||
describe('<ReactFlow />: Basic Props', () => {
|
||||
describe('uses defaultNodes and defaultEdges', () => {
|
||||
beforeEach(() => {
|
||||
cy.mount(<ReactFlow defaultNodes={simpleflow.nodes} defaultEdges={simpleflow.edges} />);
|
||||
});
|
||||
|
||||
it('mounts nodes and edges', () => {
|
||||
cy.get('.react-flow__node').should('have.length', simpleflow.nodes.length);
|
||||
cy.get('.react-flow__edge').should('have.length', simpleflow.edges.length);
|
||||
});
|
||||
|
||||
it('drags a node', () => {
|
||||
const styleBeforeDrag = Cypress.$('.react-flow__node:first').css('transform');
|
||||
|
||||
cy.drag('.react-flow__node:first', { x: 200, y: 25 }).then(($el: JQuery<HTMLElement>) => {
|
||||
const styleAfterDrag = $el.css('transform');
|
||||
expect(styleBeforeDrag).to.not.equal(styleAfterDrag);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses nodes and edges', () => {
|
||||
beforeEach(() => {
|
||||
cy.mount(
|
||||
<ControlledFlow
|
||||
addOnNodeChangeHandler={false}
|
||||
initialNodes={simpleflow.nodes}
|
||||
initialEdges={simpleflow.edges}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('mounts nodes and edges', () => {
|
||||
cy.get('.react-flow__node').should('have.length', simpleflow.nodes.length);
|
||||
cy.get('.react-flow__edge').should('have.length', simpleflow.edges.length);
|
||||
});
|
||||
|
||||
it('can not drag a node', () => {
|
||||
const styleBeforeDrag = Cypress.$('.react-flow__node:first').css('transform');
|
||||
|
||||
cy.drag('.react-flow__node:first', { x: 200, y: 25 }).then(($el: JQuery<HTMLElement>) => {
|
||||
const styleAfterDrag = $el.css('transform');
|
||||
expect(styleBeforeDrag).to.equal(styleAfterDrag);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses onNodesChange handler', () => {
|
||||
beforeEach(() => {
|
||||
cy.mount(<ControlledFlow addOnConnectHandler={false} initialNodes={simpleflow.nodes} />);
|
||||
});
|
||||
|
||||
it('mounts nodes', () => {
|
||||
cy.get('.react-flow__node').should('have.length', simpleflow.nodes.length);
|
||||
cy.get('.react-flow__edge').should('have.length', 0);
|
||||
});
|
||||
|
||||
it('drags a node', () => {
|
||||
const styleBeforeDrag = Cypress.$('.react-flow__node:first').css('transform');
|
||||
|
||||
cy.drag('.react-flow__node:first', { x: 200, y: 25 }).then(($el: JQuery<HTMLElement>) => {
|
||||
const styleAfterDrag = $el.css('transform');
|
||||
expect(styleBeforeDrag).to.not.equal(styleAfterDrag);
|
||||
});
|
||||
});
|
||||
|
||||
it('can not connect nodes', () => {
|
||||
cy.get('.react-flow__node').first().find('.react-flow__handle.source').trigger('mousedown', { button: 0 });
|
||||
|
||||
cy.get('.react-flow__node')
|
||||
.last()
|
||||
.find('.react-flow__handle.target')
|
||||
.trigger('mousemove')
|
||||
.trigger('mouseup', { force: true });
|
||||
|
||||
cy.get('.react-flow__edge').should('have.length', 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses onEdgeChange handler', () => {
|
||||
beforeEach(() => {
|
||||
cy.mount(<ControlledFlow initialNodes={simpleflow.nodes} initialEdges={simpleflow.edges} />);
|
||||
});
|
||||
|
||||
it('mounts nodes and edges', () => {
|
||||
cy.get('.react-flow__node').should('have.length', simpleflow.nodes.length);
|
||||
cy.get('.react-flow__edge').should('have.length', simpleflow.edges.length);
|
||||
});
|
||||
|
||||
it('selects an edge', () => {
|
||||
cy.get('.react-flow__edge').first().click().should('have.class', 'selected');
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses onConnect handlers', () => {
|
||||
beforeEach(() => {
|
||||
cy.mount(<ControlledFlow initialNodes={simpleflow.nodes} />);
|
||||
});
|
||||
|
||||
it('mounts nodes', () => {
|
||||
cy.get('.react-flow__node').should('have.length', simpleflow.nodes.length);
|
||||
cy.get('.react-flow__edge').should('have.length', 0);
|
||||
});
|
||||
|
||||
it('connects nodes', () => {
|
||||
cy.get('.react-flow__node').first().find('.react-flow__handle.source').trigger('mousedown', { button: 0 });
|
||||
|
||||
cy.get('.react-flow__node')
|
||||
.last()
|
||||
.find('.react-flow__handle.target')
|
||||
.trigger('mousemove')
|
||||
.trigger('mouseup', { force: true });
|
||||
|
||||
cy.get('.react-flow__edge').should('have.length', 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses nodeTypes', () => {
|
||||
it('renders custom nodes', () => {
|
||||
const nodeTypes = {
|
||||
custom: () => <div className="customnode">custom node</div>,
|
||||
};
|
||||
|
||||
const initialNodes = simpleflow.nodes.map((n) => ({
|
||||
...n,
|
||||
type: 'custom',
|
||||
}));
|
||||
|
||||
cy.mount(<ControlledFlow nodeTypes={nodeTypes} initialNodes={initialNodes} />);
|
||||
|
||||
cy.get('.react-flow__node-custom').should('have.length', simpleflow.nodes.length);
|
||||
cy.get('.react-flow').contains('custom node');
|
||||
});
|
||||
|
||||
it('tries invalid node type - should fallback to default', () => {
|
||||
const initialNodes = simpleflow.nodes.map((n) => ({
|
||||
...n,
|
||||
type: 'invalid',
|
||||
}));
|
||||
|
||||
cy.mount(<ControlledFlow initialNodes={initialNodes} />);
|
||||
|
||||
cy.get('.react-flow__node-invalid').should('have.length', 0);
|
||||
cy.get('.react-flow__node-default').should('have.length', simpleflow.nodes.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses edgeTypes', () => {
|
||||
it('renders custom edge', () => {
|
||||
const edgeTypes = {
|
||||
custom: ({ sourceX, sourceY, targetX, targetY }: EdgeProps) => (
|
||||
<path d={`M${sourceX} ${sourceY} L${targetX} ${targetY}`} stroke="black" />
|
||||
),
|
||||
};
|
||||
|
||||
const initialEdges = simpleflow.edges.map((e) => ({
|
||||
...e,
|
||||
type: 'custom',
|
||||
}));
|
||||
|
||||
cy.mount(<ControlledFlow edgeTypes={edgeTypes} initialNodes={simpleflow.nodes} initialEdges={initialEdges} />);
|
||||
|
||||
cy.get('.react-flow__edge-custom').should('have.length', simpleflow.edges.length);
|
||||
});
|
||||
|
||||
it('tries invalid edge type - should fallback to default', () => {
|
||||
const initialEdges = simpleflow.edges.map((e) => ({
|
||||
...e,
|
||||
type: 'invalid',
|
||||
}));
|
||||
|
||||
cy.mount(<ControlledFlow initialNodes={simpleflow.nodes} initialEdges={initialEdges} />);
|
||||
|
||||
cy.get('.react-flow__edge-invalid').should('have.length', 0);
|
||||
cy.get('.react-flow__edge-default').should('have.length', simpleflow.edges.length);
|
||||
});
|
||||
});
|
||||
|
||||
it('uses style', () => {
|
||||
const styles = {
|
||||
backgroundColor: 'red',
|
||||
};
|
||||
|
||||
cy.mount(<ControlledFlow style={styles} />);
|
||||
cy.get('.react-flow').should('have.css', 'background-color', 'rgb(255, 0, 0)');
|
||||
});
|
||||
|
||||
it('uses classname', () => {
|
||||
cy.mount(<ControlledFlow className="custom" />);
|
||||
cy.get('.react-flow').should('have.class', 'custom');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,210 @@
|
||||
import { skipOn } from '@cypress/skip-test';
|
||||
|
||||
import { nodes, edges } from '../../fixtures/simpleflow';
|
||||
import ControlledFlow from '../../support/ControlledFlow';
|
||||
|
||||
describe('<ReactFlow />: Event handlers', () => {
|
||||
describe('Node event handlers', () => {
|
||||
it('handles onInit', () => {
|
||||
const onInitSpy = cy.spy().as('onInitSpy');
|
||||
|
||||
cy.mount(<ControlledFlow initialNodes={nodes} initialEdges={edges} onInit={onInitSpy} />)
|
||||
.wait(100)
|
||||
.then(() => {
|
||||
expect(onInitSpy.callCount).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('handles onNodeClick', () => {
|
||||
const onNodeClickSpy = cy.spy().as('onNodeClickSpy');
|
||||
|
||||
cy.mount(<ControlledFlow initialNodes={nodes} initialEdges={edges} onNodeClick={onNodeClickSpy} />).then(() => {
|
||||
expect(onNodeClickSpy.callCount).to.be.eq(0);
|
||||
cy.get('.react-flow__node:first')
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onNodeClickSpy.callCount).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles onNodeDrag handlers', () => {
|
||||
const onNodeDragStart = cy.spy().as('onNodeDragStart');
|
||||
const onNodeDrag = cy.spy().as('onNodeDrag');
|
||||
const onNodeDragStop = cy.spy().as('onNodeDragStop');
|
||||
|
||||
cy.mount(
|
||||
<ControlledFlow
|
||||
initialNodes={nodes}
|
||||
initialEdges={edges}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDrag={onNodeDrag}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
/>
|
||||
).then(() => {
|
||||
expect(onNodeDragStart.callCount).to.be.eq(0);
|
||||
expect(onNodeDrag.callCount).to.be.eq(0);
|
||||
expect(onNodeDragStop.callCount).to.be.eq(0);
|
||||
|
||||
cy.drag('.react-flow__node:first', { x: 200, y: 0 }).then(() => {
|
||||
expect(onNodeDragStart.callCount).to.be.gt(0);
|
||||
expect(onNodeDrag.callCount).to.be.gt(0);
|
||||
expect(onNodeDragStop.callCount).to.be.gt(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles onNodeMouse handlers', () => {
|
||||
const onNodeMouseEnter = cy.spy().as('onNodeMouseEnter');
|
||||
const onNodeMouseMove = cy.spy().as('onNodeMouseMove');
|
||||
const onNodeMouseLeave = cy.spy().as('onNodeMouseLeave');
|
||||
const onNodeContextMenu = cy.spy().as('onNodeContextMenu');
|
||||
const onNodeDoubleClick = cy.spy().as('onNodeDoubleClick');
|
||||
|
||||
cy.mount(
|
||||
<ControlledFlow
|
||||
initialNodes={nodes}
|
||||
initialEdges={edges}
|
||||
onNodeMouseEnter={onNodeMouseEnter}
|
||||
onNodeMouseMove={onNodeMouseMove}
|
||||
onNodeMouseLeave={onNodeMouseLeave}
|
||||
onNodeContextMenu={onNodeContextMenu}
|
||||
onNodeDoubleClick={onNodeDoubleClick}
|
||||
/>
|
||||
)
|
||||
.wait(200)
|
||||
.then(() => {
|
||||
expect(onNodeMouseEnter.callCount).to.be.eq(0);
|
||||
expect(onNodeMouseMove.callCount).to.be.eq(0);
|
||||
expect(onNodeMouseLeave.callCount).to.be.eq(0);
|
||||
expect(onNodeContextMenu.callCount).to.be.eq(0);
|
||||
expect(onNodeDoubleClick.callCount).to.be.eq(0);
|
||||
|
||||
const node = cy.get('.react-flow__node').contains('Node 1');
|
||||
|
||||
node
|
||||
.rightclick()
|
||||
.dblclick()
|
||||
.then(() => {
|
||||
expect(onNodeContextMenu.callCount).to.be.eq(1);
|
||||
expect(onNodeDoubleClick.callCount).to.be.eq(1);
|
||||
});
|
||||
|
||||
skipOn('firefox');
|
||||
|
||||
node
|
||||
.realHover()
|
||||
.realMouseMove(100, 100)
|
||||
.then(() => {
|
||||
expect(onNodeMouseEnter.callCount).to.be.gt(0);
|
||||
expect(onNodeMouseMove.callCount).to.be.gt(0);
|
||||
expect(onNodeMouseLeave.callCount).to.be.gt(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge event handlers', () => {
|
||||
it('handles onEdgeClick', () => {
|
||||
const onEdgeClick = cy.spy().as('onEdgeClick');
|
||||
|
||||
cy.mount(<ControlledFlow initialNodes={nodes} initialEdges={edges} onEdgeClick={onEdgeClick} />).then(() => {
|
||||
expect(onEdgeClick.callCount).to.be.eq(0);
|
||||
cy.get('.react-flow__edge:first')
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onEdgeClick.callCount).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles onEdgeMouse handlers', () => {
|
||||
const onEdgeMouseEnter = cy.spy().as('onEdgeMouseEnter');
|
||||
const onEdgeMouseMove = cy.spy().as('onEdgeMouseMove');
|
||||
const onEdgeMouseLeave = cy.spy().as('onEdgeMouseLeave');
|
||||
const onEdgeContextMenu = cy.spy().as('onEdgeContextMenu');
|
||||
const onEdgeDoubleClick = cy.spy().as('onEdgedoubleClick');
|
||||
|
||||
cy.mount(
|
||||
<ControlledFlow
|
||||
initialNodes={nodes}
|
||||
initialEdges={edges}
|
||||
onEdgeMouseEnter={onEdgeMouseEnter}
|
||||
onEdgeMouseMove={onEdgeMouseMove}
|
||||
onEdgeMouseLeave={onEdgeMouseLeave}
|
||||
onEdgeContextMenu={onEdgeContextMenu}
|
||||
onEdgeDoubleClick={onEdgeDoubleClick}
|
||||
/>
|
||||
)
|
||||
.wait(200)
|
||||
.then(() => {
|
||||
expect(onEdgeMouseEnter.callCount).to.be.eq(0);
|
||||
expect(onEdgeMouseMove.callCount).to.be.eq(0);
|
||||
expect(onEdgeMouseLeave.callCount).to.be.eq(0);
|
||||
expect(onEdgeContextMenu.callCount).to.be.eq(0);
|
||||
expect(onEdgeDoubleClick.callCount).to.be.eq(0);
|
||||
|
||||
const edge = cy.get('.react-flow__edge:first');
|
||||
|
||||
edge
|
||||
.rightclick()
|
||||
.dblclick()
|
||||
.then(() => {
|
||||
expect(onEdgeContextMenu.callCount).to.be.eq(1);
|
||||
});
|
||||
|
||||
skipOn('firefox');
|
||||
|
||||
edge
|
||||
.realHover()
|
||||
.realMouseMove(100, 100)
|
||||
.then(() => {
|
||||
expect(onEdgeMouseEnter.callCount).to.be.gt(0);
|
||||
expect(onEdgeMouseMove.callCount).to.be.gt(0);
|
||||
expect(onEdgeMouseLeave.callCount).to.be.gt(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pane event handlers', () => {
|
||||
it('handles onMove handlers', () => {
|
||||
const onMoveStart = cy.spy().as('onMoveStart');
|
||||
const onMove = cy.spy().as('onMove');
|
||||
const onMoveEnd = cy.spy().as('onMoveEnd');
|
||||
|
||||
cy.mount(<ControlledFlow onMoveStart={onMoveStart} onMove={onMove} onMoveEnd={onMoveEnd} />).then(() => {
|
||||
expect(onMoveStart.callCount).to.be.eq(0);
|
||||
expect(onMove.callCount).to.be.eq(0);
|
||||
expect(onMoveEnd.callCount).to.be.eq(0);
|
||||
|
||||
cy.dragPane({ from: { x: 10, y: 200 }, to: { x: 100, y: 200 } })
|
||||
.wait(100)
|
||||
.then(() => {
|
||||
expect(onMoveStart.callCount).to.be.eq(1);
|
||||
expect(onMove.callCount).to.be.gt(0);
|
||||
expect(onMoveEnd.callCount).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles click handlers', () => {
|
||||
const onPaneClick = cy.spy().as('onPaneClick');
|
||||
const onPaneContextMenu = cy.spy().as('onPaneContextMenu');
|
||||
|
||||
cy.mount(<ControlledFlow onPaneClick={onPaneClick} onPaneContextMenu={onPaneContextMenu} />).then(() => {
|
||||
expect(onPaneClick.callCount).to.be.eq(0);
|
||||
expect(onPaneContextMenu.callCount).to.be.eq(0);
|
||||
|
||||
cy.get('.react-flow__pane')
|
||||
.rightclick()
|
||||
.click()
|
||||
.wait(100)
|
||||
.then(() => {
|
||||
expect(onPaneClick.callCount).to.be.eq(1);
|
||||
expect(onPaneContextMenu.callCount).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import { ReactFlow, BaseEdge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath, ReactFlowProvider } from '@xyflow/react';
|
||||
import * as simpleflow from '../../fixtures/simpleflow';
|
||||
|
||||
function CustomEdge(props: EdgeProps) {
|
||||
const [path, labelX, labelY] = getSmoothStepPath(props);
|
||||
return (
|
||||
<>
|
||||
<BaseEdge path={path} labelX={labelX} labelY={labelY} />
|
||||
<EdgeLabelRenderer>
|
||||
<div className="label">{props.id}</div>
|
||||
</EdgeLabelRenderer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const simpleflow1 = { ...simpleflow };
|
||||
simpleflow1.edges = [...simpleflow1.edges];
|
||||
simpleflow1.edges[0] = { ...simpleflow1.edges[0], id: 'edge1' };
|
||||
|
||||
const simpleflow2 = { ...simpleflow };
|
||||
simpleflow2.edges = [...simpleflow2.edges];
|
||||
simpleflow2.edges[0] = { ...simpleflow2.edges[0], id: 'edge2' };
|
||||
|
||||
describe('<ReactFlow />: Multiple Instances', () => {
|
||||
describe('render EdgeLabelRenderer', () => {
|
||||
beforeEach(() => {
|
||||
cy.mount(
|
||||
<>
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
defaultNodes={simpleflow1.nodes}
|
||||
edgeTypes={{ default: CustomEdge }}
|
||||
defaultEdges={simpleflow1.edges}
|
||||
/>
|
||||
</ReactFlowProvider>
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
defaultNodes={simpleflow2.nodes}
|
||||
edgeTypes={{ default: CustomEdge }}
|
||||
defaultEdges={simpleflow2.edges}
|
||||
/>
|
||||
</ReactFlowProvider>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
it('Each ReactFlow instance has one edge label in EdgeLabelRenderer', () => {
|
||||
cy.get('.react-flow__edgelabel-renderer').should('have.length', 2);
|
||||
|
||||
cy.get('.react-flow__edgelabel-renderer')
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
cy.get('.label').should('have.length', 1).should('contain.text', 'edge1');
|
||||
});
|
||||
|
||||
cy.get('.react-flow__edgelabel-renderer')
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.get('.label').should('have.length', 1).should('contain.text', 'edge2');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ReactFlow } from '@xyflow/react';
|
||||
|
||||
import { nodes, edges } from '../../fixtures/simpleflow';
|
||||
|
||||
describe('<ReactFlow />: Uncontrolled Flow', () => {
|
||||
beforeEach(() => {
|
||||
cy.mount(<ReactFlow defaultNodes={nodes} defaultEdges={edges} />);
|
||||
});
|
||||
|
||||
it('mounts nodes and edges', () => {
|
||||
cy.get('.react-flow__node').should('have.length', nodes.length);
|
||||
cy.get('.react-flow__edge').should('have.length', edges.length);
|
||||
});
|
||||
|
||||
it('selects a node', () => {
|
||||
cy.get('.react-flow__node').first().click().should('have.class', 'selected');
|
||||
cy.get('.react-flow__pane').click();
|
||||
cy.get('.react-flow__node').first().should('not.have.class', 'selected');
|
||||
});
|
||||
|
||||
it('drags a node', () => {
|
||||
const styleBeforeDrag = Cypress.$('.react-flow__node:first').css('transform');
|
||||
|
||||
cy.drag('.react-flow__node:first', { x: 200, y: 25 }).then(($el: JQuery<HTMLElement>) => {
|
||||
const styleAfterDrag = $el.css('transform');
|
||||
expect(styleBeforeDrag).to.not.equal(styleAfterDrag);
|
||||
});
|
||||
});
|
||||
|
||||
it('selects an edge', () => {
|
||||
cy.get('.react-flow__edge').first().click().should('have.class', 'selected');
|
||||
cy.get('.react-flow__pane').click();
|
||||
cy.get('.react-flow__edge').first().should('not.have.class', 'selected');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,312 @@
|
||||
import { ReactFlow, ReactFlowProps, Viewport, useViewport, SnapGrid, CoordinateExtent, Node } from '@xyflow/react';
|
||||
|
||||
import ControlledFlow from '../../support/ControlledFlow';
|
||||
import * as simpleflow from '../../fixtures/simpleflow';
|
||||
|
||||
const nodesOutsideOfView = [
|
||||
{
|
||||
...simpleflow.nodes[0],
|
||||
position: {
|
||||
x: -500,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
...simpleflow.nodes[1],
|
||||
position: {
|
||||
x: 500,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe('<ReactFlow />: View Props', () => {
|
||||
it('uses fitView', () => {
|
||||
cy.mount(<Comp nodes={nodesOutsideOfView} fitView minZoom={0.2} />).wait(200);
|
||||
cy.get('.react-flow__node:first').isWithinViewport();
|
||||
});
|
||||
|
||||
it('uses fitViewOptions', () => {
|
||||
const fitViewOptions = { padding: 0.5 };
|
||||
|
||||
cy.mount(<ReactFlow nodes={[nodesOutsideOfView[0]]} fitView fitViewOptions={fitViewOptions} />).wait(200);
|
||||
cy.get('.react-flow__node:first').isWithinViewport();
|
||||
});
|
||||
|
||||
describe('uses minZoom', () => {
|
||||
it('minZoom: 0.2', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
|
||||
cy.mount(<Comp minZoom={0.2} onChange={onChangeSpy} />);
|
||||
cy.zoomPane(10000).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(0.2);
|
||||
});
|
||||
});
|
||||
|
||||
it('minZoom: 1', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
|
||||
cy.mount(<Comp minZoom={1} onChange={onChangeSpy} />);
|
||||
cy.zoomPane(10000).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets invalid defaultZoom', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
const defaultViewport = { x: 0, y: 0, zoom: 0.2 };
|
||||
|
||||
cy.mount(<Comp minZoom={1} defaultViewport={defaultViewport} onChange={onChangeSpy} />);
|
||||
cy.zoomPane(10000).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses maxZoom', () => {
|
||||
it('maxZoom: 1', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
|
||||
cy.mount(<Comp maxZoom={1} onChange={onChangeSpy} />);
|
||||
cy.zoomPane(-10000).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('maxZoom: 2', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
|
||||
cy.mount(<Comp maxZoom={2} onChange={onChangeSpy} />);
|
||||
cy.zoomPane(-10000).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets invalid defaultZoom', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
const defaultViewport = { x: 0, y: 0, zoom: 2 };
|
||||
|
||||
cy.mount(<Comp maxZoom={1.5} defaultViewport={defaultViewport} onChange={onChangeSpy} />);
|
||||
cy.zoomPane(-10000).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(1.5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses defaultViewport', () => {
|
||||
it('defaultViewport: { x: 0, y: 0, zoom: 1 }', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
const defaultViewport = { x: 0, y: 0, zoom: 1 };
|
||||
|
||||
cy.mount(<Comp nodes={simpleflow.nodes} defaultViewport={defaultViewport} onChange={onChangeSpy} />).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy)).to.be.eql(defaultViewport);
|
||||
});
|
||||
});
|
||||
|
||||
it('defaultViewport: { x: 0, y: 0, zoom: 1.5 }', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
const defaultViewport = { x: 0, y: 0, zoom: 1.5 };
|
||||
|
||||
cy.mount(<Comp nodes={simpleflow.nodes} defaultViewport={defaultViewport} onChange={onChangeSpy} />).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy)).to.be.eql(defaultViewport);
|
||||
});
|
||||
});
|
||||
|
||||
it('defaultViewport: { x: 100, y: 100, zoom: 1.5 }', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
const defaultViewport = { x: 100, y: 100, zoom: 1.5 };
|
||||
|
||||
cy.mount(<Comp nodes={simpleflow.nodes} defaultViewport={defaultViewport} onChange={onChangeSpy} />).then(() => {
|
||||
expect(getLatestViewport(onChangeSpy)).to.be.eql(defaultViewport);
|
||||
});
|
||||
});
|
||||
|
||||
it('defaultViewport: invalid { x: 0, y: 0, zoom: -1 }', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
const defaultViewport = { x: 0, y: 0, zoom: -1 };
|
||||
|
||||
cy.mount(<Comp nodes={simpleflow.nodes} defaultViewport={defaultViewport} onChange={onChangeSpy} />).then(() => {
|
||||
// this should be min zoom because zoom is clamped
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eql(0.5);
|
||||
});
|
||||
});
|
||||
|
||||
it('defaultViewport: invalid { x: 0, y: 0, zoom: 100 }', () => {
|
||||
const onChangeSpy = cy.spy().as('onChangeSpy');
|
||||
const defaultViewport = { x: 0, y: 0, zoom: 100 };
|
||||
|
||||
cy.mount(<Comp nodes={simpleflow.nodes} defaultViewport={defaultViewport} onChange={onChangeSpy} />).then(() => {
|
||||
// this should be max zoom because zoom is clamped
|
||||
expect(getLatestViewport(onChangeSpy).zoom).to.be.eql(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('uses snapGrid and snapToGrid', () => {
|
||||
const snapGrid: SnapGrid = [100, 100];
|
||||
|
||||
cy.mount(<ControlledFlow initialNodes={simpleflow.nodes} snapGrid={snapGrid} snapToGrid />).then(() => {
|
||||
cy.drag('.react-flow__node:first', { x: 125, y: 125 }).then(($el: any) => {
|
||||
const transformAfterDrag = $el.css('transform');
|
||||
const { m41: nodeX, m42: nodeY } = new DOMMatrixReadOnly(transformAfterDrag);
|
||||
expect([nodeX, nodeY]).to.eql(snapGrid);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses onlyRenderVisibleElements', () => {
|
||||
it('displays no nodes', () => {
|
||||
cy.mount(<ControlledFlow initialNodes={nodesOutsideOfView} onlyRenderVisibleElements />).then(() => {
|
||||
cy.get('.react-flow__node').should('have.length', 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('displays one node', () => {
|
||||
const nodes = nodesOutsideOfView.map((n) => {
|
||||
if (n.id === '1') {
|
||||
return { ...n, position: { x: 0, y: 0 } };
|
||||
}
|
||||
return n;
|
||||
});
|
||||
cy.mount(<ControlledFlow initialNodes={nodes} onlyRenderVisibleElements />).then(() => {
|
||||
cy.get('.react-flow__node').should('have.length', 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('uses translateExtent', () => {
|
||||
const translateExtent: CoordinateExtent = [
|
||||
[0, 0],
|
||||
[1000, 1000],
|
||||
];
|
||||
|
||||
cy.mount(<ControlledFlow translateExtent={translateExtent} />).then(() => {
|
||||
const transformBeforeDrag = Cypress.$('.react-flow__viewport').css('transform');
|
||||
|
||||
cy.dragPane({ from: { x: 0, y: 0 }, to: { x: 100, y: 100 } }).then(() => {
|
||||
const transformAfterDrag = Cypress.$('.react-flow__viewport').css('transform');
|
||||
expect(transformBeforeDrag).to.eql(transformAfterDrag);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('uses nodeExtent', () => {
|
||||
const nodes: Node[] = [
|
||||
{
|
||||
id: '1',
|
||||
position: { x: 200, y: 200 },
|
||||
data: { label: '1' },
|
||||
},
|
||||
];
|
||||
|
||||
const nodeExtent: CoordinateExtent = [
|
||||
[0, 0],
|
||||
[50, 50],
|
||||
];
|
||||
|
||||
cy.mount(<ControlledFlow nodes={nodes} nodeExtent={nodeExtent} />)
|
||||
.wait(500)
|
||||
.then(() => {
|
||||
const transform = Cypress.$('.react-flow__node:first').css('transform');
|
||||
const { m41: nodeX, m42: nodeY } = new DOMMatrixReadOnly(transform);
|
||||
expect(nodeX).to.equal(50);
|
||||
expect(nodeY).to.equal(50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses preventScrolling', () => {
|
||||
function ScrollFlow({ preventScrolling }: { preventScrolling: boolean }) {
|
||||
return (
|
||||
<div className="wrapper" style={{ height: 2000 }}>
|
||||
<div className="inner" style={{ height: 500 }}>
|
||||
<ControlledFlow preventScrolling={preventScrolling} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
it('lets user scroll', () => {
|
||||
cy.mount(<ScrollFlow preventScrolling={false} />).then(() => {
|
||||
cy.window().then((window) => {
|
||||
cy.get('.react-flow__pane').trigger('wheel', {
|
||||
wheelDelta: 240,
|
||||
wheelDeltaX: 0,
|
||||
wheelDeltaY: 240,
|
||||
eventConstructor: 'WheelEvent',
|
||||
view: window,
|
||||
});
|
||||
|
||||
// @TODO: why is this not working?
|
||||
// it seems that the event is somehow broken. This works fine when using the mouse.
|
||||
// cy.window().its('scrollY').should('be.gt', 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not user scroll', () => {
|
||||
cy.mount(<ScrollFlow preventScrolling={true} />).then(() => {
|
||||
cy.window().then((window) => {
|
||||
cy.get('.react-flow__pane').trigger('wheel', {
|
||||
wheelDelta: 240,
|
||||
wheelDeltaX: 0,
|
||||
wheelDeltaY: 240,
|
||||
eventConstructor: 'WheelEvent',
|
||||
view: window,
|
||||
});
|
||||
|
||||
cy.window().its('scrollY').should('be.equal', 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uses attributionPosition', () => {
|
||||
it('displays it on the bottom right', () => {
|
||||
cy.mount(<ControlledFlow />).then(() => {
|
||||
cy.get('.react-flow__attribution').then(($el) => {
|
||||
const { left, top, width, height } = $el[0].getBoundingClientRect();
|
||||
|
||||
expect(left).equal(window.innerWidth - width);
|
||||
expect(top).equal(window.innerHeight - height);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('displays it on the top left', () => {
|
||||
cy.mount(<ControlledFlow attributionPosition="top-left" />).then(() => {
|
||||
cy.get('.react-flow__attribution').then(($el) => {
|
||||
const { left, top } = $el[0].getBoundingClientRect();
|
||||
|
||||
expect(left).equal(0);
|
||||
expect(top).equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// test specific helpers
|
||||
|
||||
type CompProps = {
|
||||
onChange?: (vp: Viewport) => void;
|
||||
};
|
||||
|
||||
function InnerComp({ onChange }: CompProps) {
|
||||
const viewport = useViewport();
|
||||
|
||||
onChange?.(viewport);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function Comp({ onChange, ...rest }: CompProps & ReactFlowProps) {
|
||||
return (
|
||||
<ReactFlow {...rest}>
|
||||
<InnerComp onChange={onChange} />
|
||||
</ReactFlow>
|
||||
);
|
||||
}
|
||||
|
||||
function getLatestViewport(onChangeSpy: any) {
|
||||
return onChangeSpy.lastCall.args[0] as unknown as Viewport;
|
||||
}
|
||||
Reference in New Issue
Block a user