From 84bc5090cf2f3745e8406366fa83b7c079aacfee Mon Sep 17 00:00:00 2001 From: Lavender Date: Wed, 16 Apr 2025 13:21:33 +1000 Subject: [PATCH] feature/MIG-6604 Add markers to the diagram --- src/assets/markers/marker-many.svg | 3 ++ src/assets/markers/marker-one-or-many.svg | 5 ++ src/assets/markers/marker-one.svg | 3 ++ src/components/canvas/canvas.tsx | 6 ++- src/components/canvas/use-canvas.test.tsx | 8 ++-- src/components/canvas/use-canvas.tsx | 6 ++- src/components/markers/marker-list.test.tsx | 14 ++++++ src/components/markers/marker-list.tsx | 51 +++++++++++++++++++++ src/components/markers/marker.test.tsx | 16 +++++++ src/components/markers/marker.tsx | 22 +++++++++ src/types/internal.ts | 6 +++ 11 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 src/assets/markers/marker-many.svg create mode 100644 src/assets/markers/marker-one-or-many.svg create mode 100644 src/assets/markers/marker-one.svg create mode 100644 src/components/markers/marker-list.test.tsx create mode 100644 src/components/markers/marker-list.tsx create mode 100644 src/components/markers/marker.test.tsx create mode 100644 src/components/markers/marker.tsx diff --git a/src/assets/markers/marker-many.svg b/src/assets/markers/marker-many.svg new file mode 100644 index 0000000..d3dcd18 --- /dev/null +++ b/src/assets/markers/marker-many.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/markers/marker-one-or-many.svg b/src/assets/markers/marker-one-or-many.svg new file mode 100644 index 0000000..ddbc82c --- /dev/null +++ b/src/assets/markers/marker-one-or-many.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/markers/marker-one.svg b/src/assets/markers/marker-one.svg new file mode 100644 index 0000000..abcdff6 --- /dev/null +++ b/src/assets/markers/marker-one.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/canvas/canvas.tsx b/src/components/canvas/canvas.tsx index a237439..8e763ad 100644 --- a/src/components/canvas/canvas.tsx +++ b/src/components/canvas/canvas.tsx @@ -7,9 +7,10 @@ import { Controls } from '@/components/controls/controls'; import { Edge, Node as ExternalNode } from '@/types'; import { Node } from '@/components/node/node'; import { useCanvas } from '@/components/canvas/use-canvas'; -import { InternalNode } from '@/types/internal'; +import { InternalEdge, InternalNode } from '@/types/internal'; import { FloatingEdge } from '@/components/edge/floating-edge'; import { SelfReferencingEdge } from '@/components/edge/self-referencing-edge'; +import { MarkerList } from '@/components/markers/marker-list'; const MAX_ZOOM = 3; const MIN_ZOOM = 0.1; @@ -40,7 +41,7 @@ export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges }: Pr const { initialNodes, initialEdges } = useCanvas(externalNodes, externalEdges); const [nodes, , onNodesChange] = useNodesState(initialNodes); - const [edges, , onEdgesChange] = useEdgesState(initialEdges); + const [edges, , onEdgesChange] = useEdgesState(initialEdges); return ( @@ -57,6 +58,7 @@ export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges }: Pr onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} > + diff --git a/src/components/canvas/use-canvas.test.tsx b/src/components/canvas/use-canvas.test.tsx index d2d0803..a574ad2 100644 --- a/src/components/canvas/use-canvas.test.tsx +++ b/src/components/canvas/use-canvas.test.tsx @@ -57,8 +57,8 @@ describe('use-canvas', () => { expect(result.current.initialEdges).toEqual([ { id: 'employees-to-orders', - markerEnd: 'one', - markerStart: 'many', + markerEnd: 'end-one', + markerStart: 'start-many', source: 'employees', target: 'orders', type: 'floatingEdge', @@ -72,8 +72,8 @@ describe('use-canvas', () => { id: 'employees-to-employees', source: 'employees', target: 'employees', - markerEnd: 'one', - markerStart: 'many', + markerEnd: 'end-one', + markerStart: 'start-many', type: 'selfReferencingEdge', }, ]); diff --git a/src/components/canvas/use-canvas.tsx b/src/components/canvas/use-canvas.tsx index 36c13e9..3ed253a 100644 --- a/src/components/canvas/use-canvas.tsx +++ b/src/components/canvas/use-canvas.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { Edge, Node as ExternalNode } from '@/types'; -import { InternalNode } from '@/types/internal'; +import { InternalEdge, InternalNode } from '@/types/internal'; export const useCanvas = (externalNodes: ExternalNode[], externalEdges: Edge[]) => { const initialNodes: InternalNode[] = useMemo( @@ -20,10 +20,12 @@ export const useCanvas = (externalNodes: ExternalNode[], externalEdges: Edge[]) [externalNodes], ); - const initialEdges: Edge[] = useMemo( + const initialEdges: InternalEdge[] = useMemo( () => externalEdges.map(edge => ({ ...edge, + markerStart: `start-${edge.markerStart}`, + markerEnd: `end-${edge.markerEnd}`, type: edge.source === edge.target ? 'selfReferencingEdge' : 'floatingEdge', })), [externalEdges], diff --git a/src/components/markers/marker-list.test.tsx b/src/components/markers/marker-list.test.tsx new file mode 100644 index 0000000..e9c6339 --- /dev/null +++ b/src/components/markers/marker-list.test.tsx @@ -0,0 +1,14 @@ +import { render, screen } from '@/mocks/testing-utils'; +import { MarkerList } from '@/components/markers/marker-list'; + +describe('marker-list', () => { + it('Should have markers', () => { + render(); + expect(screen.getByTestId('start-many')).toBeInTheDocument(); + expect(screen.getByTestId('start-one')).toBeInTheDocument(); + expect(screen.getByTestId('start-oneOrMany')).toBeInTheDocument(); + expect(screen.getByTestId('end-many')).toBeInTheDocument(); + expect(screen.getByTestId('end-one')).toBeInTheDocument(); + expect(screen.getByTestId('end-oneOrMany')).toBeInTheDocument(); + }); +}); diff --git a/src/components/markers/marker-list.tsx b/src/components/markers/marker-list.tsx new file mode 100644 index 0000000..293791b --- /dev/null +++ b/src/components/markers/marker-list.tsx @@ -0,0 +1,51 @@ +import { ReactNode } from 'react'; + +import { Marker } from '@/components/markers/marker'; +import MarkerOneOrMany from '@/assets/markers/marker-one-or-many.svg?react'; +import MarkerMany from '@/assets/markers/marker-many.svg?react'; +import MarkerOne from '@/assets/markers/marker-one.svg?react'; + +interface MarkerProps extends React.SVGAttributes { + component: ReactNode; +} + +const markerList: Record = { + 'start-oneOrMany': { + component: , + orient: 'auto-start-reverse', + }, + 'end-oneOrMany': { + component: , + orient: 'auto', + }, + 'start-one': { + component: , + orient: 'auto-start-reverse', + }, + 'end-one': { + component: , + orient: 'auto', + }, + 'start-many': { + component: , + orient: 'auto-start-reverse', + }, + 'end-many': { + component: , + orient: 'auto', + }, +}; + +export const MarkerList = () => { + return ( + + + {Object.entries(markerList).map(([id, { component, orient }]) => ( + + {component} + + ))} + + + ); +}; diff --git a/src/components/markers/marker.test.tsx b/src/components/markers/marker.test.tsx new file mode 100644 index 0000000..e57dafb --- /dev/null +++ b/src/components/markers/marker.test.tsx @@ -0,0 +1,16 @@ +import { render, screen } from '@/mocks/testing-utils'; +import { Marker } from '@/components/markers/marker'; + +describe('marker', () => { + it('Should render marker', () => { + render(); + const marker = screen.getByTestId('marker'); + expect(marker).toBeInTheDocument(); + expect(marker).toHaveAttribute('markerHeight', '15'); + expect(marker).toHaveAttribute('markerWidth', '15'); + expect(marker).toHaveAttribute('refX', '7.5'); + expect(marker).toHaveAttribute('refY', '7.5'); + expect(marker).toHaveAttribute('orient', 'auto'); + expect(marker).toHaveAttribute('fill', '#FFF'); + }); +}); diff --git a/src/components/markers/marker.tsx b/src/components/markers/marker.tsx new file mode 100644 index 0000000..0e7f5ef --- /dev/null +++ b/src/components/markers/marker.tsx @@ -0,0 +1,22 @@ +import { PropsWithChildren } from 'react'; +import { palette } from '@leafygreen-ui/palette'; + +import { DEFAULT_MARKER_SIZE } from '@/utilities/constants'; + +type Props = PropsWithChildren>; + +export const Marker = ({ children, id, ...rest }: Props) => { + return ( + + {children} + + ); +}; diff --git a/src/types/internal.ts b/src/types/internal.ts index f73331c..8d88dde 100644 --- a/src/types/internal.ts +++ b/src/types/internal.ts @@ -1,6 +1,7 @@ import { Node as ReactFlowNode } from '@xyflow/react'; import { NodeBorderVariant, NodeField } from '@/types/node'; +import { BaseEdgeProps } from '@/types/edge'; export type NodeData = { title: string; @@ -8,3 +9,8 @@ export type NodeData = { borderVariant?: NodeBorderVariant; }; export type InternalNode = ReactFlowNode; + +export interface InternalEdge extends BaseEdgeProps { + markerStart: 'start-one' | 'start-oneOrMany' | 'start-many'; + markerEnd: 'end-one' | 'end-oneOrMany' | 'end-many'; +}