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:
Moritz Klack
2023-08-31 16:44:27 +02:00
committed by GitHub
parent 3da34c739e
commit dcc38794a6
197 changed files with 2003 additions and 1575 deletions
@@ -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;
}