Skip to content

Commit c6adadd

Browse files
authored
feature/MIG-6725 Add minor bug fixes to the diagram component (#32)
* feature/MIG-6725 Add minor bug fixes to the diagram * feature/MIG-6725 Fixing unit tests * feature/MIG-6725 Ensure consistent border size * feature/MIG-6725 Fix linting * feature/MIG-6725 Add tests, fix the storybook and dataset
1 parent c6e1834 commit c6adadd

File tree

10 files changed

+138
-28
lines changed

10 files changed

+138
-28
lines changed

src/components/canvas/canvas.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ const edgeTypes = {
4444
selfReferencingEdge: SelfReferencingEdge,
4545
};
4646

47-
type Props = Pick<ReactFlowProps, 'title' | 'onConnect'> & { nodes: ExternalNode[]; edges: EdgeProps[] };
47+
type Props = Pick<ReactFlowProps, 'title' | 'onConnect' | 'id'> & { nodes: ExternalNode[]; edges: EdgeProps[] };
4848

49-
export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges, onConnect, ...rest }: Props) => {
49+
export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges, onConnect, id, ...rest }: Props) => {
5050
const { initialNodes, initialEdges } = useCanvas(externalNodes, externalEdges);
5151

5252
const [nodes, setNodes, onNodesChange] = useNodesState<InternalNode>(initialNodes);
@@ -65,6 +65,7 @@ export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges, onCo
6565
return (
6666
<ReactFlowWrapper>
6767
<ReactFlow
68+
id={id}
6869
title={title}
6970
proOptions={PRO_OPTIONS}
7071
maxZoom={MAX_ZOOM}
@@ -84,7 +85,7 @@ export const Canvas = ({ title, nodes: externalNodes, edges: externalEdges, onCo
8485
{...rest}
8586
>
8687
<MarkerList />
87-
<Background />
88+
<Background id={id} />
8889
<Controls title={title} />
8990
<MiniMap />
9091
</ReactFlow>

src/components/canvas/use-canvas.test.tsx

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ describe('use-canvas', () => {
1717
},
1818
draggable: false,
1919
connectable: false,
20+
selectable: true,
2021
measured: {
2122
height: 36,
2223
width: 244,
2324
},
2425
data: {
25-
borderVariant: undefined,
2626
disabled: true,
2727
fields: [
2828
{ name: 'ORDER_ID', type: 'varchar', glyphs: ['key'] },
@@ -44,9 +44,8 @@ describe('use-canvas', () => {
4444
},
4545
draggable: true,
4646
connectable: false,
47+
selectable: true,
4748
data: {
48-
disabled: undefined,
49-
borderVariant: undefined,
5049
fields: [
5150
{ name: 'employeeId', type: 'objectId', glyphs: ['key'] },
5251
{ name: 'employeeDetail', type: '{}' },
@@ -58,6 +57,59 @@ describe('use-canvas', () => {
5857
},
5958
]);
6059
});
60+
it('Should be connectable', () => {
61+
const { result } = renderHook(() => useCanvas([{ ...ORDERS_NODE, connectable: true }], []));
62+
expect(result.current.initialNodes).toEqual([
63+
{
64+
id: 'orders',
65+
type: 'table',
66+
position: {
67+
x: 100,
68+
y: 100,
69+
},
70+
draggable: false,
71+
connectable: true,
72+
measured: {
73+
height: 36,
74+
width: 244,
75+
},
76+
data: {
77+
fields: [
78+
{ name: 'ORDER_ID', type: 'varchar', glyphs: ['key'] },
79+
{ name: 'SUPPLIER_ID', type: 'varchar', glyphs: ['link'] },
80+
],
81+
title: 'orders',
82+
},
83+
},
84+
]);
85+
});
86+
it('Should be selectable', () => {
87+
const { result } = renderHook(() => useCanvas([{ ...ORDERS_NODE, selectable: true }], []));
88+
expect(result.current.initialNodes).toEqual([
89+
{
90+
id: 'orders',
91+
type: 'table',
92+
position: {
93+
x: 100,
94+
y: 100,
95+
},
96+
draggable: true,
97+
selectable: true,
98+
connectable: false,
99+
measured: {
100+
height: 36,
101+
width: 244,
102+
},
103+
data: {
104+
fields: [
105+
{ name: 'ORDER_ID', type: 'varchar', glyphs: ['key'] },
106+
{ name: 'SUPPLIER_ID', type: 'varchar', glyphs: ['link'] },
107+
],
108+
title: 'orders',
109+
},
110+
},
111+
]);
112+
});
61113
it('Should get initial floating edges', () => {
62114
const { result } = renderHook(() => useCanvas([], [ORDERS_TO_EMPLOYEES_EDGE]));
63115
expect(result.current.initialEdges).toEqual([

src/components/canvas/use-canvas.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ export const useCanvas = (externalNodes: ExternalNode[], externalEdges: EdgeProp
77
const initialNodes: InternalNode[] = useMemo(
88
() =>
99
externalNodes.map(node => {
10-
const { title, fields, borderVariant, disabled, connectable, ...rest } = node;
10+
const { title, fields, borderVariant, disabled, connectable, selectable, ...rest } = node;
1111
return {
1212
...rest,
13-
draggable: !disabled,
14-
connectable: connectable || false,
13+
draggable: !disabled && !connectable,
14+
selectable: !connectable || selectable,
15+
connectable: connectable ?? false,
1516
data: {
1617
title,
1718
disabled,

src/components/controls/controls.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const ControlsGroup = styled.div`
2121
> .react-flow__controls-button {
2222
background-color: ${props => props.theme.controls.background};
2323
border: 1px solid ${palette.gray.base};
24+
height: ${spacing[600] + spacing[100]}px;
25+
width: ${spacing[600] + spacing[100]}px;
2426
color: ${props => props.theme.controls.buttonColor};
2527
}
2628
> .react-flow__controls-button:hover {

src/components/diagram.stories.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useState } from 'react';
33
import { Connection } from '@xyflow/react';
44

55
import { Diagram } from '@/components/diagram';
6-
import { EMPLOYEES_NODE, ORDERS_NODE } from '@/mocks/datasets/nodes';
6+
import { EMPLOYEE_TERRITORIES_NODE, EMPLOYEES_NODE, ORDERS_NODE } from '@/mocks/datasets/nodes';
77
import { EMPLOYEES_TO_EMPLOYEES_EDGE, ORDERS_TO_EMPLOYEES_EDGE } from '@/mocks/datasets/edges';
88
import { EdgeProps } from '@/types';
99

@@ -14,7 +14,7 @@ const diagram: Meta<typeof Diagram> = {
1414
title: 'MongoDB Diagram',
1515
isDarkMode: true,
1616
edges: [ORDERS_TO_EMPLOYEES_EDGE, EMPLOYEES_TO_EMPLOYEES_EDGE],
17-
nodes: [ORDERS_NODE, EMPLOYEES_NODE],
17+
nodes: [ORDERS_NODE, EMPLOYEES_NODE, EMPLOYEE_TERRITORIES_NODE],
1818
},
1919
};
2020

@@ -27,7 +27,7 @@ export const DiagramWithConnectableNodes: Story = {
2727
const [edges, setEdges] = useState<EdgeProps[]>(context.args.edges);
2828
const onConnect = (connection: Connection) => {
2929
setEdges([
30-
...edges,
30+
...edges.filter(edge => edge.source === connection.source && edge.source === connection.target),
3131
{
3232
...ORDERS_TO_EMPLOYEES_EDGE,
3333
source: connection.source,
@@ -57,6 +57,9 @@ export const DiagramWithConnectableNodes: Story = {
5757
title: 'MongoDB Diagram',
5858
isDarkMode: true,
5959
edges: [],
60-
nodes: [ORDERS_NODE, EMPLOYEES_NODE],
60+
nodes: [
61+
{ ...ORDERS_NODE, connectable: true },
62+
{ ...EMPLOYEES_NODE, connectable: true },
63+
],
6164
},
6265
};

src/components/field/field-list.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ export const FieldList = ({ fields, nodeType, isHovering }: Props) => {
2222
const previewGroupLength = getPreviewGroupLengths(fields);
2323
return (
2424
<NodeFieldWrapper>
25-
{fields.map(({ name, type: fieldType, hoverVariant, depth, glyphs, variant }) => (
25+
{fields.map(({ name, type: fieldType, hoverVariant, depth, glyphs, variant }, i) => (
2626
<Field
27-
key={name}
27+
key={i}
2828
name={name}
2929
nodeType={nodeType}
3030
hoverVariant={hoverVariant}

src/components/node/node-border.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import { animatedBlueBorder } from '@/styles/styles';
1010

1111
const borderProperties = css`
1212
border-radius: ${spacing[200]}px;
13-
width: ${DEFAULT_NODE_WIDTH + spacing[50]}px;
13+
`;
14+
15+
export const Border = styled.div`
16+
width: ${DEFAULT_NODE_WIDTH}px;
1417
`;
1518

1619
export const AnimatedBorder = styled.div<{ height?: string }>`
@@ -41,8 +44,16 @@ export const NodeBorder = ({ children, variant }: PropsWithChildren<Props>) => {
4144
};
4245

4346
if (variant === 'preview') {
44-
return <AnimatedBorder>{children}</AnimatedBorder>;
47+
return (
48+
<Border>
49+
<AnimatedBorder>{children}</AnimatedBorder>
50+
</Border>
51+
);
4552
}
4653

47-
return <BasicBorder outlineBorderColor={getBasicBorderColor()}>{children}</BasicBorder>;
54+
return (
55+
<Border>
56+
<BasicBorder outlineBorderColor={getBasicBorderColor()}>{children}</BasicBorder>
57+
</Border>
58+
);
4859
};

src/components/node/node.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Handle, NodeProps, Position, useViewport } from '@xyflow/react';
1+
import { Handle, NodeProps, Position, useStore, useViewport } from '@xyflow/react';
22
import styled from '@emotion/styled';
33
import { fontFamilies, spacing } from '@leafygreen-ui/tokens';
44
import { useTheme } from '@emotion/react';
@@ -10,7 +10,6 @@ import {
1010
DEFAULT_FIELD_HEIGHT,
1111
DEFAULT_FIELD_PADDING,
1212
DEFAULT_NODE_HEADER_HEIGHT,
13-
DEFAULT_NODE_WIDTH,
1413
ZOOM_THRESHOLD,
1514
} from '@/utilities/constants';
1615
import { InternalNode } from '@/types/internal';
@@ -28,6 +27,8 @@ const NodeZoomedOut = styled.div<{ height: number }>`
2827
const NodeZoomedOutInner = styled.div`
2928
font-size: 20px;
3029
text-align: center;
30+
padding-left: ${spacing[300]}px;
31+
padding-right: ${spacing[300]}px;
3132
${ellipsisTruncation}
3233
`;
3334

@@ -36,7 +37,6 @@ const NodeWrapper = styled.div<{ accent: string; color: string }>`
3637
font-family: ${fontFamilies.code};
3738
background: ${props => props.theme.node.background};
3839
color: ${props => props.color};
39-
width: ${DEFAULT_NODE_WIDTH}px;
4040
overflow: hidden;
4141
border-left: 1px solid ${props => props.accent};
4242
border: 1px solid ${props => props.theme.node.border};
@@ -78,18 +78,16 @@ const NodeHeaderTitle = styled.div`
7878
${ellipsisTruncation}
7979
`;
8080

81-
const NodeHandle = styled(Handle)`
81+
const NodeHandle = styled(Handle)<{ zIndex?: number }>`
8282
width: 100%;
8383
height: 100%;
84-
background: blue;
8584
position: absolute;
8685
top: 0;
8786
left: 0;
8887
border-radius: 0;
8988
transform: none;
90-
border: none;
9189
opacity: 0;
92-
z-index: 1;
90+
z-index: ${props => props.zIndex};
9391
`;
9492

9593
export const Node = ({
@@ -130,6 +128,8 @@ export const Node = ({
130128

131129
const isContextualZoom = zoom < ZOOM_THRESHOLD;
132130

131+
const fromHandle = useStore(state => state.connection.fromHandle);
132+
133133
const onMouseEnter = () => {
134134
setHovering(true);
135135
};
@@ -141,8 +141,20 @@ export const Node = ({
141141
return (
142142
<div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
143143
<NodeBorder variant={selected ? 'selected' : borderVariant}>
144-
<NodeHandle id="source" isConnectable={isConnectable} position={Position.Right} type="source" />
145-
<NodeHandle id="source" isConnectable={isConnectable} position={Position.Left} type="target" />
144+
<NodeHandle
145+
id="source"
146+
isConnectable={isConnectable}
147+
position={Position.Right}
148+
type="source"
149+
zIndex={fromHandle ? 0 : 1}
150+
/>
151+
<NodeHandle
152+
id="target"
153+
isConnectable={isConnectable}
154+
position={Position.Left}
155+
type="target"
156+
zIndex={fromHandle ? 1 : 0}
157+
/>
146158
<NodeWrapper accent={getAccent()} color={getNodeColor()}>
147159
<NodeHeader background={getHeaderBackground()}>
148160
{!isContextualZoom && (

src/mocks/datasets/nodes.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,21 @@ export const EMPLOYEES_NODE: NodeProps = {
3838
{ name: 'lastName', type: 'string', depth: 1 },
3939
],
4040
};
41+
42+
export const EMPLOYEE_TERRITORIES_NODE: NodeProps = {
43+
id: 'employee_territories',
44+
type: 'table',
45+
position: {
46+
x: 400,
47+
y: 100,
48+
},
49+
measured: {
50+
width: DEFAULT_NODE_WIDTH,
51+
height: DEFAULT_FIELD_HEIGHT * 4,
52+
},
53+
title: 'employee_territories',
54+
fields: [
55+
{ name: 'employeeId', type: 'string', glyphs: ['key'] },
56+
{ name: 'employeeTerritory', type: 'string', glyphs: ['key'] },
57+
],
58+
};

src/types/node.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,17 @@ import { NodeData } from '@/types/internal';
44

55
export type BaseNodeProps = Pick<
66
ReactFlowNode<{}, NodeType>,
7-
'id' | 'type' | 'position' | 'hidden' | 'draggable' | 'connectable' | 'selected' | 'style' | 'className' | 'measured'
7+
| 'id'
8+
| 'type'
9+
| 'position'
10+
| 'hidden'
11+
| 'draggable'
12+
| 'connectable'
13+
| 'selected'
14+
| 'selectable'
15+
| 'style'
16+
| 'className'
17+
| 'measured'
818
>;
919
export type NodeType = 'table' | 'collection';
1020
export type NodeBorderVariant = 'subtle' | 'preview' | 'selected' | 'none';

0 commit comments

Comments
 (0)