Skip to content

Commit b10708b

Browse files
committed
feature/MIG-6992 Change zoom level
1 parent c746812 commit b10708b

File tree

4 files changed

+156
-52
lines changed

4 files changed

+156
-52
lines changed

src/components/diagram.stories.tsx

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Meta, StoryObj } from '@storybook/react';
2-
import { useState } from 'react';
3-
import { Connection } from '@xyflow/react';
42

53
import { Diagram } from '@/components/diagram';
64
import { EMPLOYEE_TERRITORIES_NODE, EMPLOYEES_NODE, ORDERS_NODE } from '@/mocks/datasets/nodes';
75
import { EMPLOYEES_TO_EMPLOYEES_EDGE, ORDERS_TO_EMPLOYEES_EDGE } from '@/mocks/datasets/edges';
8-
import { EdgeProps } from '@/types';
6+
import { DiagramStressTestDecorator } from '@/mocks/decorators/diagram-stress-test.decorator';
7+
import { DiagramConnectableDecorator } from '@/mocks/decorators/diagram-connectable.decorator';
98

109
const diagram: Meta<typeof Diagram> = {
1110
title: 'Diagram',
@@ -22,37 +21,7 @@ export default diagram;
2221
type Story = StoryObj<typeof Diagram>;
2322
export const BasicDiagram: Story = {};
2423
export const DiagramWithConnectableNodes: Story = {
25-
decorators: [
26-
(Story, context) => {
27-
const [edges, setEdges] = useState<EdgeProps[]>(context.args.edges);
28-
const onConnect = (connection: Connection) => {
29-
setEdges([
30-
...edges.filter(edge => edge.source === connection.source && edge.source === connection.target),
31-
{
32-
...ORDERS_TO_EMPLOYEES_EDGE,
33-
source: connection.source,
34-
target: connection.target,
35-
animated: true,
36-
selected: true,
37-
},
38-
]);
39-
};
40-
41-
const onPaneClick = () => {
42-
setEdges(edges.filter(edge => edge.id !== ORDERS_TO_EMPLOYEES_EDGE.id));
43-
};
44-
45-
return Story({
46-
...context,
47-
args: {
48-
...context.args,
49-
edges,
50-
onPaneClick,
51-
onConnect,
52-
},
53-
});
54-
},
55-
],
24+
decorators: [DiagramConnectableDecorator],
5625
args: {
5726
title: 'MongoDB Diagram',
5827
isDarkMode: true,
@@ -63,3 +32,13 @@ export const DiagramWithConnectableNodes: Story = {
6332
],
6433
},
6534
};
35+
36+
export const DiagramStressTest: Story = {
37+
decorators: [DiagramStressTestDecorator],
38+
args: {
39+
title: 'MongoDB Diagram',
40+
isDarkMode: true,
41+
edges: [],
42+
nodes: [],
43+
},
44+
};

src/components/node/node.tsx

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { useTheme } from '@emotion/react';
55
import Icon from '@leafygreen-ui/icon';
66
import { useState } from 'react';
77

8-
import { ellipsisTruncation } from '@/styles/styles';
98
import {
109
DEFAULT_FIELD_HEIGHT,
1110
DEFAULT_FIELD_PADDING,
@@ -21,15 +20,25 @@ const NodeZoomedOut = styled.div<{ height: number }>`
2120
display: flex;
2221
align-items: center;
2322
justify-content: center;
24-
height: ${props => props.height}px;
23+
24+
min-height: ${props => props.height}px;
2525
`;
2626

27-
const NodeZoomedOutInner = styled.div`
28-
font-size: 20px;
27+
const NodeZoomedOutInner = styled.div<{ fontSize: number }>`
28+
font-size: ${props => props.fontSize}px;
2929
text-align: center;
30+
min-width: 0;
3031
padding-left: ${spacing[300]}px;
3132
padding-right: ${spacing[300]}px;
32-
${ellipsisTruncation}
33+
display: -webkit-box;
34+
-webkit-line-clamp: 3;
35+
-webkit-box-orient: vertical;
36+
overflow: hidden;
37+
text-overflow: ellipsis;
38+
word-break: break-word;
39+
overflow-wrap: break-word;
40+
padding-top: ${spacing[200]}px;
41+
padding-bottom: ${spacing[200]}px;
3342
`;
3443

3544
const NodeWrapper = styled.div<{ accent: string; color: string; background: string }>`
@@ -134,6 +143,12 @@ export const Node = ({
134143
}
135144
};
136145

146+
const getNodeHeight = () => {
147+
const fieldHeight = fields.length * DEFAULT_FIELD_HEIGHT + DEFAULT_FIELD_PADDING * 2;
148+
const titleHeight = title.length / 2;
149+
return fieldHeight + titleHeight;
150+
};
151+
137152
const isContextualZoom = zoom < ZOOM_THRESHOLD;
138153

139154
const fromHandle = useStore(state => state.connection.fromHandle);
@@ -164,22 +179,28 @@ export const Node = ({
164179
z-index={fromHandle ? 1 : 0}
165180
/>
166181
<NodeWrapper accent={getAccent()} color={getNodeColor()} background={getNodeBackground()}>
167-
<NodeHeader background={getHeaderBackground()}>
168-
{!isContextualZoom && (
169-
<>
170-
<NodeHeaderIcon>
171-
<Icon fill={theme.node.headerIcon} glyph="Drag" />
172-
</NodeHeaderIcon>
173-
<NodeHeaderTitle>{title}</NodeHeaderTitle>
174-
</>
175-
)}
176-
</NodeHeader>
177182
{isContextualZoom && (
178-
<NodeZoomedOut height={fields.length * DEFAULT_FIELD_HEIGHT + DEFAULT_FIELD_PADDING * 2}>
179-
<NodeZoomedOutInner title={title}>{title}</NodeZoomedOutInner>
183+
<NodeZoomedOut height={getNodeHeight()}>
184+
<NodeZoomedOutInner fontSize={zoom < 0.5 ? 20 : 30} title={title}>
185+
{title}
186+
</NodeZoomedOutInner>
180187
</NodeZoomedOut>
181188
)}
182-
{!isContextualZoom && <FieldList nodeType={type as NodeType} isHovering={isHovering} fields={fields} />}
189+
{!isContextualZoom && (
190+
<>
191+
<NodeHeader background={getHeaderBackground()}>
192+
{!isContextualZoom && (
193+
<>
194+
<NodeHeaderIcon>
195+
<Icon fill={theme.node.headerIcon} glyph="Drag" />
196+
</NodeHeaderIcon>
197+
<NodeHeaderTitle>{title}</NodeHeaderTitle>
198+
</>
199+
)}
200+
</NodeHeader>
201+
<FieldList nodeType={type as NodeType} isHovering={isHovering} fields={fields} />
202+
</>
203+
)}
183204
</NodeWrapper>
184205
</NodeBorder>
185206
</div>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useState } from 'react';
2+
import { Connection } from '@xyflow/react';
3+
import { Decorator } from '@storybook/react';
4+
5+
import { DiagramProps, EdgeProps } from '@/types';
6+
import { ORDERS_TO_EMPLOYEES_EDGE } from '@/mocks/datasets/edges';
7+
8+
export const DiagramConnectableDecorator: Decorator<DiagramProps> = (Story, context) => {
9+
const [edges, setEdges] = useState<EdgeProps[]>(context.args.edges);
10+
const onConnect = (connection: Connection) => {
11+
setEdges([
12+
...edges.filter(edge => edge.source === connection.source && edge.source === connection.target),
13+
{
14+
...ORDERS_TO_EMPLOYEES_EDGE,
15+
source: connection.source,
16+
target: connection.target,
17+
animated: true,
18+
selected: true,
19+
},
20+
]);
21+
};
22+
23+
const onPaneClick = () => {
24+
setEdges(edges.filter(edge => edge.id !== ORDERS_TO_EMPLOYEES_EDGE.id));
25+
};
26+
27+
return Story({
28+
...context,
29+
args: {
30+
...context.args,
31+
edges,
32+
onPaneClick,
33+
onConnect,
34+
},
35+
});
36+
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useEffect, useState } from 'react';
2+
import { Decorator } from '@storybook/react';
3+
4+
import { DiagramProps, EdgeProps, NodeProps } from '@/types';
5+
import { applyLayout } from '@/utilities/apply-layout';
6+
7+
const names = [
8+
'users',
9+
'logs',
10+
'orders_2025_q3',
11+
'product_inventory',
12+
'customer_feedback_surveys',
13+
'internal_metrics_weekly_rollup',
14+
'auth_sessions_archive_legacy_system',
15+
'enterprise_tenant_billing_transactions_us_east_1',
16+
'chat_messages_daily_aggregation_snapshot',
17+
'api_keys',
18+
];
19+
20+
export const DiagramStressTestDecorator: Decorator<DiagramProps> = (Story, context) => {
21+
const [nodes, setNodes] = useState<NodeProps[]>([]);
22+
const [edges, setEdges] = useState<EdgeProps[]>([]);
23+
24+
useEffect(() => {
25+
const nodes = generateNodes(100);
26+
const edges = generateEdges(nodes);
27+
28+
applyLayout<NodeProps, EdgeProps>(nodes, edges, 'STAR').then(result => {
29+
setNodes(result.nodes);
30+
setEdges(result.edges);
31+
});
32+
}, []);
33+
34+
const generateEdges = (nodes: NodeProps[]): EdgeProps[] => {
35+
return nodes.map(node => ({
36+
id: `edge_${node.id}`,
37+
source: nodes[Math.floor(Math.random() * nodes.length)].id,
38+
target: nodes[Math.floor(Math.random() * nodes.length)].id,
39+
markerStart: 'many',
40+
markerEnd: 'one',
41+
}));
42+
};
43+
44+
const generateNodes = (count: number): NodeProps[] => {
45+
return Array.from(Array(count).keys()).map(i => ({
46+
id: `node_${i}`,
47+
type: 'table',
48+
position: {
49+
x: 0,
50+
y: 0,
51+
},
52+
title: names[Math.floor(Math.random() * names.length)],
53+
fields: Array.from(Array(1 + (i % 9)).keys()).map(_ => ({
54+
name: names[Math.floor(Math.random() * names.length)],
55+
type: 'varchar',
56+
})),
57+
}));
58+
};
59+
60+
return Story({
61+
...context,
62+
args: {
63+
...context.args,
64+
nodes,
65+
edges,
66+
},
67+
});
68+
};

0 commit comments

Comments
 (0)