Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/assets/markers/marker-many.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/markers/marker-one-or-many.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/markers/marker-one.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions src/components/canvas/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -40,7 +41,7 @@ export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges }: Pr
const { initialNodes, initialEdges } = useCanvas(externalNodes, externalEdges);

const [nodes, , onNodesChange] = useNodesState<InternalNode>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge>(initialEdges);
const [edges, , onEdgesChange] = useEdgesState<InternalEdge>(initialEdges);

return (
<ReactFlowWrapper>
Expand All @@ -57,6 +58,7 @@ export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges }: Pr
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
>
<MarkerList />
<Background />
<Controls title={title} />
<MiniMap />
Expand Down
8 changes: 4 additions & 4 deletions src/components/canvas/use-canvas.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
},
]);
Expand Down
6 changes: 4 additions & 2 deletions src/components/canvas/use-canvas.tsx
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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],
Expand Down
14 changes: 14 additions & 0 deletions src/components/markers/marker-list.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<MarkerList />);
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();
});
});
51 changes: 51 additions & 0 deletions src/components/markers/marker-list.tsx
Original file line number Diff line number Diff line change
@@ -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<SVGMarkerElement> {
component: ReactNode;
}

const markerList: Record<string, MarkerProps> = {
'start-oneOrMany': {
component: <MarkerOneOrMany />,
orient: 'auto-start-reverse',
},
'end-oneOrMany': {
component: <MarkerOneOrMany />,
orient: 'auto',
},
'start-one': {
component: <MarkerOne />,
orient: 'auto-start-reverse',
},
'end-one': {
component: <MarkerOne />,
orient: 'auto',
},
'start-many': {
component: <MarkerMany />,
orient: 'auto-start-reverse',
},
'end-many': {
component: <MarkerMany />,
orient: 'auto',
},
};

export const MarkerList = () => {
return (
<svg>
<defs>
{Object.entries(markerList).map(([id, { component, orient }]) => (
<Marker key={id} data-testid={id} orient={orient} id={id}>
{component}
</Marker>
))}
</defs>
</svg>
);
};
16 changes: 16 additions & 0 deletions src/components/markers/marker.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { render, screen } from '@/mocks/testing-utils';
import { Marker } from '@/components/markers/marker';

describe('marker', () => {
it('Should render marker', () => {
render(<Marker id={'orders-to-employees'} data-testid={'marker'} orient={'auto'} fill={'#FFF'} />);
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');
});
});
22 changes: 22 additions & 0 deletions src/components/markers/marker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { PropsWithChildren } from 'react';
import { palette } from '@leafygreen-ui/palette';

import { DEFAULT_MARKER_SIZE } from '@/utilities/constants';

type Props = PropsWithChildren<React.SVGAttributes<SVGMarkerElement>>;

export const Marker = ({ children, id, ...rest }: Props) => {
return (
<marker
id={id}
style={{ fill: palette.gray.base }}
markerHeight={DEFAULT_MARKER_SIZE}
markerWidth={DEFAULT_MARKER_SIZE}
refX={DEFAULT_MARKER_SIZE / 2}
refY={DEFAULT_MARKER_SIZE / 2}
{...rest}
>
{children}
</marker>
);
};
6 changes: 6 additions & 0 deletions src/types/internal.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { Node as ReactFlowNode } from '@xyflow/react';

import { NodeBorderVariant, NodeField } from '@/types/node';
import { BaseEdgeProps } from '@/types/edge';

export type NodeData = {
title: string;
fields: Array<NodeField>;
borderVariant?: NodeBorderVariant;
};
export type InternalNode = ReactFlowNode<NodeData>;

export interface InternalEdge extends BaseEdgeProps {
markerStart: 'start-one' | 'start-oneOrMany' | 'start-many';
markerEnd: 'end-one' | 'end-oneOrMany' | 'end-many';
}