import ReactFlow, { ReactFlowProps, Viewport, useViewport, SnapGrid, CoordinateExtent, Node } from 'reactflow'; 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(': View Props', () => { it('uses fitView', () => { cy.mount().wait(200); cy.get('.react-flow__node:first').isWithinViewport(); }); it('uses fitViewOptions', () => { const fitViewOptions = { padding: 0.5 }; cy.mount().wait(200); cy.get('.react-flow__node:first').isWithinViewport(); }); describe('uses minZoom', () => { it('minZoom: 0.2', () => { const onChangeSpy = cy.spy().as('onChangeSpy'); cy.mount(); cy.zoomPane(10000).then(() => { expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(0.2); }); }); it('minZoom: 1', () => { const onChangeSpy = cy.spy().as('onChangeSpy'); cy.mount(); 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(); 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(); cy.zoomPane(-10000).then(() => { expect(getLatestViewport(onChangeSpy).zoom).to.be.eq(1); }); }); it('maxZoom: 2', () => { const onChangeSpy = cy.spy().as('onChangeSpy'); cy.mount(); 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(); 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().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().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().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().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().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().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().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().then(() => { cy.get('.react-flow__node').should('have.length', 1); }); }); }); it('uses translateExtent', () => { const translateExtent: CoordinateExtent = [ [0, 0], [1000, 1000], ]; cy.mount().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() .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 (
); } it('lets user scroll', () => { cy.mount().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().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().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().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 ( ); } function getLatestViewport(onChangeSpy: any) { return onChangeSpy.lastCall.args[0] as unknown as Viewport; }