refactored react-examples app and created generic test case component

This commit is contained in:
Peter
2023-11-07 16:19:09 +01:00
parent b8a75ccac6
commit 8be0337e00
7 changed files with 299 additions and 70 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ import { defineConfig } from 'cypress';
export default defineConfig({ export default defineConfig({
e2e: { e2e: {
baseUrl: 'http://localhost:3000', baseUrl: 'http://localhost:3000/examples',
viewportWidth: 1280, viewportWidth: 1280,
viewportHeight: 720, viewportHeight: 720,
video: false, video: false,
+81 -69
View File
@@ -1,5 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { BrowserRouter, Route, Routes, useLocation, useNavigate } from 'react-router-dom'; import { BrowserRouter, Outlet, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import GenericTests from '../generic-tests';
import Basic from '../examples/Basic'; import Basic from '../examples/Basic';
import Backgrounds from '../examples/Backgrounds'; import Backgrounds from '../examples/Backgrounds';
@@ -57,232 +59,232 @@ interface IRoute {
const routes: IRoute[] = [ const routes: IRoute[] = [
{ {
name: 'Basic', name: 'Basic',
path: '/', path: 'basic',
component: Basic, component: Basic,
}, },
{ {
name: 'Backgrounds', name: 'Backgrounds',
path: '/backgrounds', path: 'backgrounds',
component: Backgrounds, component: Backgrounds,
}, },
{ {
name: 'Cancel Connection', name: 'Cancel Connection',
path: '/cancel-connection', path: 'cancel-connection',
component: CancelConnection, component: CancelConnection,
}, },
{ {
name: 'Controlled/Uncontrolled', name: 'Controlled/Uncontrolled',
path: '/controlled-uncontrolled', path: 'controlled-uncontrolled',
component: ControlledUncontrolled, component: ControlledUncontrolled,
}, },
{ {
name: 'Controlled Viewport', name: 'Controlled Viewport',
path: '/controlled-viewport', path: 'controlled-viewport',
component: ControlledViewport, component: ControlledViewport,
}, },
{ {
name: 'Custom Connection Line', name: 'Custom Connection Line',
path: '/custom-connectionline', path: 'custom-connectionline',
component: CustomConnectionLine, component: CustomConnectionLine,
}, },
{ {
name: 'Custom Minimap Node', name: 'Custom Minimap Node',
path: '/custom-minimap-node', path: 'custom-minimap-node',
component: CustomMiniMapNode, component: CustomMiniMapNode,
}, },
{ {
name: 'Custom Node', name: 'Custom Node',
path: '/custom-node', path: 'custom-node',
component: CustomNode, component: CustomNode,
}, },
{ {
name: 'Default Nodes', name: 'Default Nodes',
path: '/default-nodes', path: 'default-nodes',
component: DefaultNodes, component: DefaultNodes,
}, },
{ {
name: 'Drag Handle', name: 'Drag Handle',
path: '/draghandle', path: 'draghandle',
component: DragHandle, component: DragHandle,
}, },
{ {
name: 'Drag and Drop', name: 'Drag and Drop',
path: '/dragndrop', path: 'dragndrop',
component: DragNDrop, component: DragNDrop,
}, },
{ {
name: 'EasyConnect', name: 'EasyConnect',
path: '/easy-connect', path: 'easy-connect',
component: EasyConnect, component: EasyConnect,
}, },
{ {
name: 'Edges', name: 'Edges',
path: '/edges', path: 'edges',
component: Edges, component: Edges,
}, },
{ {
name: 'Edge Renderer', name: 'Edge Renderer',
path: '/edge-renderer', path: 'edge-renderer',
component: EdgeRenderer, component: EdgeRenderer,
}, },
{ {
name: 'Edge Types', name: 'Edge Types',
path: '/edge-types', path: 'edge-types',
component: EdgeTypes, component: EdgeTypes,
}, },
{ {
name: 'Edge Routing', name: 'Edge Routing',
path: '/edge-routing', path: 'edge-routing',
component: EdgeRouting, component: EdgeRouting,
}, },
{ {
name: 'Empty', name: 'Empty',
path: '/empty', path: 'empty',
component: Empty, component: Empty,
}, },
{ {
name: 'Figma', name: 'Figma',
path: '/figma', path: 'figma',
component: Figma, component: Figma,
}, },
{ {
name: 'Floating Edges', name: 'Floating Edges',
path: '/floating-edges', path: 'floating-edges',
component: FloatingEdges, component: FloatingEdges,
}, },
{ {
name: 'Hidden', name: 'Hidden',
path: '/hidden', path: 'hidden',
component: Hidden, component: Hidden,
}, },
{ {
name: 'Interaction', name: 'Interaction',
path: '/interaction', path: 'interaction',
component: Interaction, component: Interaction,
}, },
{ {
name: 'Intersection', name: 'Intersection',
path: '/intersection', path: 'intersection',
component: Intersection, component: Intersection,
}, },
{ {
name: 'Interactive Minimap', name: 'Interactive Minimap',
path: '/interactive-minimap', path: 'interactive-minimap',
component: InteractiveMinimap, component: InteractiveMinimap,
}, },
{ {
name: 'Layouting', name: 'Layouting',
path: '/layouting', path: 'layouting',
component: Layouting, component: Layouting,
}, },
{ {
name: 'Multi Flows', name: 'Multi Flows',
path: '/multiflows', path: 'multiflows',
component: MultiFlows, component: MultiFlows,
}, },
{ {
name: 'Nested Nodes', name: 'Nested Nodes',
path: '/nested-nodes', path: 'nested-nodes',
component: NestedNodes, component: NestedNodes,
}, },
{ {
name: 'Node Type Change', name: 'Node Type Change',
path: '/nodetype-change', path: 'nodetype-change',
component: NodeTypeChange, component: NodeTypeChange,
}, },
{ {
name: 'nodeTypes Object Change', name: 'nodeTypes Object Change',
path: '/nodetypesobject-change', path: 'nodetypesobject-change',
component: NodeTypesObjectChange, component: NodeTypesObjectChange,
}, },
{ {
name: 'NodeToolbar', name: 'NodeToolbar',
path: '/node-toolbar', path: 'node-toolbar',
component: NodeToolbar, component: NodeToolbar,
}, },
{ {
name: 'NodeResizer', name: 'NodeResizer',
path: '/node-resizer', path: 'node-resizer',
component: NodeResizer, component: NodeResizer,
}, },
{ {
name: 'Overview', name: 'Overview',
path: '/overview', path: 'overview',
component: Overview, component: Overview,
}, },
{ {
name: 'Provider', name: 'Provider',
path: '/provider', path: 'provider',
component: Provider, component: Provider,
}, },
{ {
name: 'Save/Restore', name: 'Save/Restore',
path: '/save-restore', path: 'save-restore',
component: SaveRestore, component: SaveRestore,
}, },
{ {
name: 'Stress', name: 'Stress',
path: '/stress', path: 'stress',
component: Stress, component: Stress,
}, },
{ {
name: 'Subflow', name: 'Subflow',
path: '/subflow', path: 'subflow',
component: Subflow, component: Subflow,
}, },
{ {
name: 'Switch Flow', name: 'Switch Flow',
path: '/switch', path: 'switch',
component: SwitchFlow, component: SwitchFlow,
}, },
{ {
name: 'Touch Device', name: 'Touch Device',
path: '/touch-device', path: 'touch-device',
component: TouchDevice, component: TouchDevice,
}, },
{ {
name: 'Undirectional', name: 'Undirectional',
path: '/undirectional', path: 'undirectional',
component: Undirectional, component: Undirectional,
}, },
{ {
name: 'Updatable Edge', name: 'Updatable Edge',
path: '/updatable-edge', path: 'updatable-edge',
component: UpdatableEdge, component: UpdatableEdge,
}, },
{ {
name: 'Update Node', name: 'Update Node',
path: '/update-node', path: 'update-node',
component: UpdateNode, component: UpdateNode,
}, },
{ {
name: 'useNodesInitialized', name: 'useNodesInitialized',
path: '/use-nodes-initialized', path: 'use-nodes-initialized',
component: useNodesInitialized, component: useNodesInitialized,
}, },
{ {
name: 'useOnSelectionChange', name: 'useOnSelectionChange',
path: '/use-on-selection-change', path: 'use-on-selection-change',
component: UseOnSelectionChange, component: UseOnSelectionChange,
}, },
{ {
name: 'useReactFlow', name: 'useReactFlow',
path: '/usereactflow', path: 'usereactflow',
component: UseReactFlow, component: UseReactFlow,
}, },
{ {
name: 'useUpdateNodeInternals', name: 'useUpdateNodeInternals',
path: '/useupdatenodeinternals', path: 'useupdatenodeinternals',
component: UseUpdateNodeInternals, component: UseUpdateNodeInternals,
}, },
{ {
name: 'Validation', name: 'Validation',
path: '/validation', path: 'validation',
component: Validation, component: Validation,
}, },
{ {
name: 'useKeyPress', name: 'useKeyPress',
path: '/use-key-press', path: 'use-key-press',
component: UseKeyPress, component: UseKeyPress,
}, },
]; ];
@@ -299,28 +301,38 @@ const Header = () => {
}, [currentPath]); }, [currentPath]);
return ( return (
<header> <>
<a className="logo" href="https://github.com/wbkd/react-flow"> <header>
React Flow Dev <a className="logo" href="https://github.com/wbkd/react-flow">
</a> React Flow Dev
<select value={currentPath} onChange={(event) => setCurrentPath(event.target.value)}> </a>
{routes.map((route) => ( <select value={currentPath} onChange={(event) => setCurrentPath(event.target.value)}>
<option value={route.path} key={route.path}> {routes.map((route) => (
{route.name} <option value={route.path} key={route.path}>
</option> {route.name}
))} </option>
</select> ))}
</header> </select>
</header>
<Outlet />
</>
); );
}; };
export default () => ( export default () => {
<BrowserRouter> return (
<Header /> <>
<Routes> <BrowserRouter>
{routes.map((route) => ( <Routes>
<Route path={route.path} key={route.path} element={<route.component />} /> <Route path="examples" element={<Header />}>
))} <Route index element={<Basic />} />
</Routes> {routes.map((route) => (
</BrowserRouter> <Route path={route.path} key={route.path} element={<route.component />} />
); ))}
</Route>
<Route path="tests/generic/*" element={<GenericTests />} />
</Routes>
</BrowserRouter>
</>
);
};
+7
View File
@@ -0,0 +1,7 @@
import { Edge, Node, ReactFlowProps } from '@xyflow/react';
declare global {
interface GenericTestCase {
reactFlowProps: Omit<ReactFlowProps, 'nodes' | 'edges'> & { nodes: Node[]; edges: Edge[] };
}
}
+29
View File
@@ -0,0 +1,29 @@
import { useState, useCallback } from 'react';
import { ReactFlow, Controls, Background, applyNodeChanges, applyEdgeChanges } from '@xyflow/react';
type FlowProps = {
generics: GenericTestCase;
};
export default ({ generics }: FlowProps) => {
const [nodes, setNodes] = useState(generics.reactFlowProps.nodes);
const [edges, setEdges] = useState(generics.reactFlowProps.edges);
const onNodesChange = useCallback((changes) => setNodes((nds) => applyNodeChanges(changes, nds)), []);
const onEdgesChange = useCallback((changes) => setEdges((eds) => applyEdgeChanges(changes, eds)), []);
return (
<div style={{ height: '100%' }}>
<ReactFlow
{...generics.reactFlowProps}
nodes={nodes}
onNodesChange={onNodesChange}
edges={edges}
onEdgesChange={onEdgesChange}
>
<Background />
<Controls />
</ReactFlow>
</div>
);
};
@@ -0,0 +1,163 @@
import { MarkerType } from '@xyflow/react';
export default {
reactFlowProps: {
fitView: true,
nodes: [
{
id: '1',
data: { label: '1' },
position: { x: 0, y: 0 },
type: 'input',
},
{
id: '2',
data: { label: '2' },
position: { x: -100, y: 100 },
},
{
id: '3',
data: { label: '3' },
position: { x: 100, y: 100 },
},
{
id: '4',
data: { label: '4' },
position: { x: -100, y: 200 },
},
{
id: '5',
data: { label: '5' },
position: { x: 100, y: 200 },
},
{
id: '6',
data: { label: '6' },
position: { x: -100, y: 300 },
},
{
id: '7',
data: { label: '7' },
position: { x: 100, y: 300 },
},
{
id: '8',
data: { label: '8' },
position: { x: -100, y: 400 },
},
{
id: '9',
data: { label: '9' },
position: { x: 100, y: 400 },
},
{
id: '10',
data: { label: '10' },
position: { x: -100, y: 500 },
},
{
id: '11',
data: { label: '11' },
position: { x: 100, y: 500 },
},
// {
// id: '12',
// data: { label: '12' },
// position: { x: -100, y: 600 }
// },
// {
// id: '13',
// data: { label: '13' },
// position: { x: 100, y: 600 }
// }
],
edges: [
{
id: 'edge-with-class',
source: '1',
target: '2',
className: 'edge-class-test',
},
{
id: 'edge-with-style',
source: '1',
target: '3',
style: {
stroke: 'red',
},
},
{
id: 'hidden-edge',
source: '2',
target: '4',
label: 'hidden',
hidden: true,
},
{
id: 'animated-edge',
source: '3',
target: '5',
label: 'animated',
animated: true,
},
{
id: 'not-selectable-edge',
source: '4',
target: '6',
label: 'not-selectable',
selectable: false,
},
{
id: 'not-deletable',
source: '5',
target: '7',
label: 'not-deletable',
deletable: false,
},
{
id: 'z-index',
source: '6',
target: '8',
label: 'z-index',
zIndex: 3141592,
},
{
id: 'aria-label',
source: '7',
target: '9',
label: 'aria-label',
ariaLabel: 'aria-label-test',
},
{
id: 'interaction-width',
source: '8',
target: '10',
label: 'interaction-width',
interactionWidth: 42,
},
{
id: 'markers',
source: '9',
target: '11',
label: 'markers',
markerEnd: { type: MarkerType.Arrow },
markerStart: { type: MarkerType.ArrowClosed },
},
// {
// id: 'updatable',
// source: '9',
// target: '11',
// label: 'focusable',
// updatable: true
// },
// {
// id: 'not-focusable',
//
// source: '5',
// target: '7',
// label: 'not-focusable',
// focusable: false
// },
],
},
} satisfies GenericTestCase;
@@ -0,0 +1,17 @@
import { useLocation } from 'react-router-dom';
import Flow from './Flow';
export default () => {
const location = useLocation();
const files = import.meta.glob<GenericTestCase>('./**/*.ts', { eager: true, import: 'default' });
const testCasePath = `.${location.pathname.replace('/tests/generic', '')}.ts`;
const testCase = files[testCasePath];
if (!testCase) {
return <div></div>;
}
return <Flow generics={testCase} />;
};
@@ -0,0 +1 @@
export default {};